]> git.ipfire.org Git - thirdparty/git.git/blobdiff - compat/mingw.c
Merge branch 'en/ort-perf-batch-9'
[thirdparty/git.git] / compat / mingw.c
index a469508952a22fd824601c4a962e1a11451378b8..a43599841c6c6bb3e4312d53e35647ed0c8af3fb 100644 (file)
 
 static const int delay[] = { 0, 1, 10, 20, 40 };
 
+void open_in_gdb(void)
+{
+       static struct child_process cp = CHILD_PROCESS_INIT;
+       extern char *_pgmptr;
+
+       strvec_pushl(&cp.args, "mintty", "gdb", NULL);
+       strvec_pushf(&cp.args, "--pid=%d", getpid());
+       cp.clean_on_exit = 1;
+       if (start_command(&cp) < 0)
+               die_errno("Could not start gdb");
+       sleep(1);
+}
+
 int err_win_to_posix(DWORD winerr)
 {
        int error = ENOSYS;
@@ -114,6 +127,7 @@ int err_win_to_posix(DWORD winerr)
        case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break;
        case ERROR_SHARING_VIOLATION: error = EACCES; break;
        case ERROR_STACK_OVERFLOW: error = ENOMEM; break;
+       case ERROR_SUCCESS: BUG("err_win_to_posix() called without an error!");
        case ERROR_SWAPERROR: error = ENOENT; break;
        case ERROR_TOO_MANY_MODULES: error = EMFILE; break;
        case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break;
@@ -212,6 +226,7 @@ enum hide_dotfiles_type {
        HIDE_DOTFILES_DOTGITONLY
 };
 
+static int core_restrict_inherited_handles = -1;
 static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
 static char *unset_environment_variables;
 
@@ -231,6 +246,15 @@ int mingw_core_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (!strcmp(var, "core.restrictinheritedhandles")) {
+               if (value && !strcasecmp(value, "auto"))
+                       core_restrict_inherited_handles = -1;
+               else
+                       core_restrict_inherited_handles =
+                               git_config_bool(var, value);
+               return 0;
+       }
+
        return 0;
 }
 
@@ -266,6 +290,9 @@ int mingw_unlink(const char *pathname)
        if (xutftowcs_path(wpathname, pathname) < 0)
                return -1;
 
+       if (DeleteFileW(wpathname))
+               return 0;
+
        /* read-only files cannot be removed */
        _wchmod(wpathname, 0666);
        while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
@@ -365,6 +392,8 @@ static inline int needs_hiding(const char *path)
                        /* ignore trailing slashes */
                        if (*path)
                                basename = path;
+                       else
+                               break;
                }
 
        if (hide_dotfiles == HIDE_DOTFILES_TRUE)
@@ -393,7 +422,7 @@ int mingw_mkdir(const char *path, int mode)
        int ret;
        wchar_t wpath[MAX_PATH];
 
-       if (!is_valid_win32_path(path)) {
+       if (!is_valid_win32_path(path, 0)) {
                errno = EINVAL;
                return -1;
        }
@@ -436,8 +465,21 @@ static int mingw_open_append(wchar_t const *wfilename, int oflags, ...)
        handle = CreateFileW(wfilename, FILE_APPEND_DATA,
                        FILE_SHARE_WRITE | FILE_SHARE_READ,
                        NULL, create, FILE_ATTRIBUTE_NORMAL, NULL);
-       if (handle == INVALID_HANDLE_VALUE)
-               return errno = err_win_to_posix(GetLastError()), -1;
+       if (handle == INVALID_HANDLE_VALUE) {
+               DWORD err = GetLastError();
+
+               /*
+                * Some network storage solutions (e.g. Isilon) might return
+                * ERROR_INVALID_PARAMETER instead of expected error
+                * ERROR_PATH_NOT_FOUND, which results in an unknown error. If
+                * so, let's turn the error to ERROR_PATH_NOT_FOUND instead.
+                */
+               if (err == ERROR_INVALID_PARAMETER)
+                       err = ERROR_PATH_NOT_FOUND;
+
+               errno = err_win_to_posix(err);
+               return -1;
+       }
 
        /*
         * No O_APPEND here, because the CRT uses it only to reset the
@@ -479,21 +521,21 @@ int mingw_open (const char *filename, int oflags, ...)
        mode = va_arg(args, int);
        va_end(args);
 
-       if (!is_valid_win32_path(filename)) {
+       if (!is_valid_win32_path(filename, !create)) {
                errno = create ? EINVAL : ENOENT;
                return -1;
        }
 
-       if (filename && !strcmp(filename, "/dev/null"))
-               filename = "nul";
-
        if ((oflags & O_APPEND) && !is_local_named_pipe_path(filename))
                open_fn = mingw_open_append;
        else
                open_fn = _wopen;
 
-       if (xutftowcs_path(wfilename, filename) < 0)
+       if (filename && !strcmp(filename, "/dev/null"))
+               wcscpy(wfilename, L"nul");
+       else if (xutftowcs_path(wfilename, filename) < 0)
                return -1;
+
        fd = open_fn(wfilename, oflags, mode);
 
        if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
@@ -550,16 +592,18 @@ FILE *mingw_fopen (const char *filename, const char *otype)
        int hide = needs_hiding(filename);
        FILE *file;
        wchar_t wfilename[MAX_PATH], wotype[4];
-       if (!is_valid_win32_path(filename)) {
+       if (filename && !strcmp(filename, "/dev/null"))
+               wcscpy(wfilename, L"nul");
+       else if (!is_valid_win32_path(filename, 1)) {
                int create = otype && strchr(otype, 'w');
                errno = create ? EINVAL : ENOENT;
                return NULL;
-       }
-       if (filename && !strcmp(filename, "/dev/null"))
-               filename = "nul";
-       if (xutftowcs_path(wfilename, filename) < 0 ||
-               xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
+       } else if (xutftowcs_path(wfilename, filename) < 0)
+               return NULL;
+
+       if (xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
                return NULL;
+
        if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
                error("could not unhide %s", filename);
                return NULL;
@@ -577,16 +621,18 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
        int hide = needs_hiding(filename);
        FILE *file;
        wchar_t wfilename[MAX_PATH], wotype[4];
-       if (!is_valid_win32_path(filename)) {
+       if (filename && !strcmp(filename, "/dev/null"))
+               wcscpy(wfilename, L"nul");
+       else if (!is_valid_win32_path(filename, 1)) {
                int create = otype && strchr(otype, 'w');
                errno = create ? EINVAL : ENOENT;
                return NULL;
-       }
-       if (filename && !strcmp(filename, "/dev/null"))
-               filename = "nul";
-       if (xutftowcs_path(wfilename, filename) < 0 ||
-               xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
+       } else if (xutftowcs_path(wfilename, filename) < 0)
+               return NULL;
+
+       if (xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
                return NULL;
+
        if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
                error("could not unhide %s", filename);
                return NULL;
@@ -936,7 +982,16 @@ revert_attrs:
 size_t mingw_strftime(char *s, size_t max,
                      const char *format, const struct tm *tm)
 {
-       size_t ret = strftime(s, max, format, tm);
+       /* a pointer to the original strftime in case we can't find the UCRT version */
+       static size_t (*fallback)(char *, size_t, const char *, const struct tm *) = strftime;
+       size_t ret;
+       DECLARE_PROC_ADDR(ucrtbase.dll, size_t, strftime, char *, size_t,
+               const char *, const struct tm *);
+
+       if (INIT_PROC_ADDR(strftime))
+               ret = strftime(s, max, format, tm);
+       else
+               ret = fallback(s, max, format, tm);
 
        if (!ret && errno == EINVAL)
                die("invalid strftime format: '%s'", format);
@@ -1007,16 +1062,16 @@ int pipe(int filedes[2])
 
 struct tm *gmtime_r(const time_t *timep, struct tm *result)
 {
-       /* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */
-       memcpy(result, gmtime(timep), sizeof(struct tm));
-       return result;
+       if (gmtime_s(result, timep) == 0)
+               return result;
+       return NULL;
 }
 
 struct tm *localtime_r(const time_t *timep, struct tm *result)
 {
-       /* localtime() in MSVCRT.DLL is thread-safe, but not reentrant */
-       memcpy(result, localtime(timep), sizeof(struct tm));
-       return result;
+       if (localtime_s(result, timep) == 0)
+               return result;
+       return NULL;
 }
 
 char *mingw_getcwd(char *pointer, int len)
@@ -1188,14 +1243,21 @@ static char *lookup_prog(const char *dir, int dirlen, const char *cmd,
                         int isexe, int exe_only)
 {
        char path[MAX_PATH];
+       wchar_t wpath[MAX_PATH];
        snprintf(path, sizeof(path), "%.*s\\%s.exe", dirlen, dir, cmd);
 
-       if (!isexe && access(path, F_OK) == 0)
+       if (xutftowcs_path(wpath, path) < 0)
+               return NULL;
+
+       if (!isexe && _waccess(wpath, F_OK) == 0)
                return xstrdup(path);
-       path[strlen(path)-4] = '\0';
-       if ((!exe_only || isexe) && access(path, F_OK) == 0)
-               if (!(GetFileAttributes(path) & FILE_ATTRIBUTE_DIRECTORY))
+       wpath[wcslen(wpath)-4] = '\0';
+       if ((!exe_only || isexe) && _waccess(wpath, F_OK) == 0) {
+               if (!(GetFileAttributesW(wpath) & FILE_ATTRIBUTE_DIRECTORY)) {
+                       path[strlen(path)-4] = '\0';
                        return xstrdup(path);
+               }
+       }
        return NULL;
 }
 
@@ -1210,7 +1272,7 @@ static char *path_lookup(const char *cmd, int exe_only)
        int len = strlen(cmd);
        int isexe = len >= 4 && !strcasecmp(cmd+len-4, ".exe");
 
-       if (strchr(cmd, '/') || strchr(cmd, '\\'))
+       if (strpbrk(cmd, "/\\"))
                return xstrdup(cmd);
 
        path = mingw_getenv("PATH");
@@ -1256,11 +1318,6 @@ static int wenvcmp(const void *a, const void *b)
        return _wcsnicmp(p, q, p_len);
 }
 
-/* We need a stable sort to convert the environment between UTF-16 <-> UTF-8 */
-#ifndef INTERNAL_QSORT
-#include "qsort.c"
-#endif
-
 /*
  * Build an environment block combining the inherited environment
  * merged with the given list of settings.
@@ -1292,15 +1349,15 @@ static wchar_t *make_environment_block(char **deltaenv)
                }
 
                ALLOC_ARRAY(result, size);
-               memcpy(result, wenv, size * sizeof(*wenv));
+               COPY_ARRAY(result, wenv, size);
                FreeEnvironmentStringsW(wenv);
                return result;
        }
 
        /*
         * If there is a deltaenv, let's accumulate all keys into `array`,
-        * sort them using the stable git_qsort() and then copy, skipping
-        * duplicate keys
+        * sort them using the stable git_stable_qsort() and then copy,
+        * skipping duplicate keys
         */
        for (p = wenv; p && *p; ) {
                ALLOC_GROW(array, nr + 1, alloc);
@@ -1323,7 +1380,7 @@ static wchar_t *make_environment_block(char **deltaenv)
                p += wlen + 1;
        }
 
-       git_qsort(array, nr, sizeof(*array), wenvcmp);
+       git_stable_qsort(array, nr, sizeof(*array), wenvcmp);
        ALLOC_ARRAY(result, size + delta_size);
 
        for (p = result, i = 0; i < nr; i++) {
@@ -1336,7 +1393,7 @@ static wchar_t *make_environment_block(char **deltaenv)
                        continue;
 
                size = wcslen(array[i]) + 1;
-               memcpy(p, array[i], size * sizeof(*p));
+               COPY_ARRAY(p, array[i], size);
                p += size;
        }
        *p = L'\0';
@@ -1434,8 +1491,13 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
                              const char *dir,
                              int prepend_cmd, int fhin, int fhout, int fherr)
 {
-       STARTUPINFOW si;
+       static int restrict_handle_inheritance = -1;
+       STARTUPINFOEXW si;
        PROCESS_INFORMATION pi;
+       LPPROC_THREAD_ATTRIBUTE_LIST attr_list = NULL;
+       HANDLE stdhandles[3];
+       DWORD stdhandles_count = 0;
+       SIZE_T size;
        struct strbuf args;
        wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs, *wenvblk = NULL;
        unsigned flags = CREATE_UNICODE_ENVIRONMENT;
@@ -1444,11 +1506,25 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
        const char *(*quote_arg)(const char *arg) =
                is_msys2_sh(cmd ? cmd : *argv) ?
                quote_arg_msys2 : quote_arg_msvc;
+       const char *strace_env;
+
+       /* Make sure to override previous errors, if any */
+       errno = 0;
+
+       if (restrict_handle_inheritance < 0)
+               restrict_handle_inheritance = core_restrict_inherited_handles;
+       /*
+        * The following code to restrict which handles are inherited seems
+        * to work properly only on Windows 7 and later, so let's disable it
+        * on Windows Vista and 2008.
+        */
+       if (restrict_handle_inheritance < 0)
+               restrict_handle_inheritance = GetVersion() >> 16 >= 7601;
 
        do_unset_environment_variables();
 
        /* Determine whether or not we are associated to a console */
-       cons = CreateFile("CONOUT$", GENERIC_WRITE,
+       cons = CreateFileW(L"CONOUT$", GENERIC_WRITE,
                        FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
                        FILE_ATTRIBUTE_NORMAL, NULL);
        if (cons == INVALID_HANDLE_VALUE) {
@@ -1472,13 +1548,27 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
                CloseHandle(cons);
        }
        memset(&si, 0, sizeof(si));
-       si.cb = sizeof(si);
-       si.dwFlags = STARTF_USESTDHANDLES;
-       si.hStdInput = winansi_get_osfhandle(fhin);
-       si.hStdOutput = winansi_get_osfhandle(fhout);
-       si.hStdError = winansi_get_osfhandle(fherr);
-
-       if (xutftowcs_path(wcmd, cmd) < 0)
+       si.StartupInfo.cb = sizeof(si);
+       si.StartupInfo.hStdInput = winansi_get_osfhandle(fhin);
+       si.StartupInfo.hStdOutput = winansi_get_osfhandle(fhout);
+       si.StartupInfo.hStdError = winansi_get_osfhandle(fherr);
+
+       /* The list of handles cannot contain duplicates */
+       if (si.StartupInfo.hStdInput != INVALID_HANDLE_VALUE)
+               stdhandles[stdhandles_count++] = si.StartupInfo.hStdInput;
+       if (si.StartupInfo.hStdOutput != INVALID_HANDLE_VALUE &&
+           si.StartupInfo.hStdOutput != si.StartupInfo.hStdInput)
+               stdhandles[stdhandles_count++] = si.StartupInfo.hStdOutput;
+       if (si.StartupInfo.hStdError != INVALID_HANDLE_VALUE &&
+           si.StartupInfo.hStdError != si.StartupInfo.hStdInput &&
+           si.StartupInfo.hStdError != si.StartupInfo.hStdOutput)
+               stdhandles[stdhandles_count++] = si.StartupInfo.hStdError;
+       if (stdhandles_count)
+               si.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
+
+       if (*argv && !strcmp(cmd, *argv))
+               wcmd[0] = L'\0';
+       else if (xutftowcs_path(wcmd, cmd) < 0)
                return -1;
        if (dir && xutftowcs_path(wdir, dir) < 0)
                return -1;
@@ -1500,6 +1590,31 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
                        free(quoted);
        }
 
+       strace_env = getenv("GIT_STRACE_COMMANDS");
+       if (strace_env) {
+               char *p = path_lookup("strace.exe", 1);
+               if (!p)
+                       return error("strace not found!");
+               if (xutftowcs_path(wcmd, p) < 0) {
+                       free(p);
+                       return -1;
+               }
+               free(p);
+               if (!strcmp("1", strace_env) ||
+                   !strcasecmp("yes", strace_env) ||
+                   !strcasecmp("true", strace_env))
+                       strbuf_insert(&args, 0, "strace ", 7);
+               else {
+                       const char *quoted = quote_arg(strace_env);
+                       struct strbuf buf = STRBUF_INIT;
+                       strbuf_addf(&buf, "strace -o %s ", quoted);
+                       if (quoted != strace_env)
+                               free((char *)quoted);
+                       strbuf_insert(&args, 0, buf.buf, buf.len);
+                       strbuf_release(&buf);
+               }
+       }
+
        ALLOC_ARRAY(wargs, st_add(st_mult(2, args.len), 1));
        xutftowcs(wargs, args.buf, 2 * args.len + 1);
        strbuf_release(&args);
@@ -1507,16 +1622,98 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
        wenvblk = make_environment_block(deltaenv);
 
        memset(&pi, 0, sizeof(pi));
-       ret = CreateProcessW(wcmd, wargs, NULL, NULL, TRUE, flags,
-               wenvblk, dir ? wdir : NULL, &si, &pi);
+       if (restrict_handle_inheritance && stdhandles_count &&
+           (InitializeProcThreadAttributeList(NULL, 1, 0, &size) ||
+            GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
+           (attr_list = (LPPROC_THREAD_ATTRIBUTE_LIST)
+                       (HeapAlloc(GetProcessHeap(), 0, size))) &&
+           InitializeProcThreadAttributeList(attr_list, 1, 0, &size) &&
+           UpdateProcThreadAttribute(attr_list, 0,
+                                     PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
+                                     stdhandles,
+                                     stdhandles_count * sizeof(HANDLE),
+                                     NULL, NULL)) {
+               si.lpAttributeList = attr_list;
+               flags |= EXTENDED_STARTUPINFO_PRESENT;
+       }
+
+       ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL,
+                            stdhandles_count ? TRUE : FALSE,
+                            flags, wenvblk, dir ? wdir : NULL,
+                            &si.StartupInfo, &pi);
+
+       /*
+        * On Windows 2008 R2, it seems that specifying certain types of handles
+        * (such as FILE_TYPE_CHAR or FILE_TYPE_PIPE) will always produce an
+        * error. Rather than playing finicky and fragile games, let's just try
+        * to detect this situation and simply try again without restricting any
+        * handle inheritance. This is still better than failing to create
+        * processes.
+        */
+       if (!ret && restrict_handle_inheritance && stdhandles_count) {
+               DWORD err = GetLastError();
+               struct strbuf buf = STRBUF_INIT;
+
+               if (err != ERROR_NO_SYSTEM_RESOURCES &&
+                   /*
+                    * On Windows 7 and earlier, handles on pipes and character
+                    * devices are inherited automatically, and cannot be
+                    * specified in the thread handle list. Rather than trying
+                    * to catch each and every corner case (and running the
+                    * chance of *still* forgetting a few), let's just fall
+                    * back to creating the process without trying to limit the
+                    * handle inheritance.
+                    */
+                   !(err == ERROR_INVALID_PARAMETER &&
+                     GetVersion() >> 16 < 9200) &&
+                   !getenv("SUPPRESS_HANDLE_INHERITANCE_WARNING")) {
+                       DWORD fl = 0;
+                       int i;
+
+                       setenv("SUPPRESS_HANDLE_INHERITANCE_WARNING", "1", 1);
+
+                       for (i = 0; i < stdhandles_count; i++) {
+                               HANDLE h = stdhandles[i];
+                               strbuf_addf(&buf, "handle #%d: %p (type %lx, "
+                                           "handle info (%d) %lx\n", i, h,
+                                           GetFileType(h),
+                                           GetHandleInformation(h, &fl),
+                                           fl);
+                       }
+                       strbuf_addstr(&buf, "\nThis is a bug; please report it "
+                                     "at\nhttps://github.com/git-for-windows/"
+                                     "git/issues/new\n\n"
+                                     "To suppress this warning, please set "
+                                     "the environment variable\n\n"
+                                     "\tSUPPRESS_HANDLE_INHERITANCE_WARNING=1"
+                                     "\n");
+               }
+               restrict_handle_inheritance = 0;
+               flags &= ~EXTENDED_STARTUPINFO_PRESENT;
+               ret = CreateProcessW(*wcmd ? wcmd : NULL, wargs, NULL, NULL,
+                                    TRUE, flags, wenvblk, dir ? wdir : NULL,
+                                    &si.StartupInfo, &pi);
+               if (!ret)
+                       errno = err_win_to_posix(GetLastError());
+               if (ret && buf.len) {
+                       warning("failed to restrict file handles (%ld)\n\n%s",
+                               err, buf.buf);
+               }
+               strbuf_release(&buf);
+       } else if (!ret)
+               errno = err_win_to_posix(GetLastError());
+
+       if (si.lpAttributeList)
+               DeleteProcThreadAttributeList(si.lpAttributeList);
+       if (attr_list)
+               HeapFree(GetProcessHeap(), 0, attr_list);
 
        free(wenvblk);
        free(wargs);
 
-       if (!ret) {
-               errno = ENOENT;
+       if (!ret)
                return -1;
-       }
+
        CloseHandle(pi.hThread);
 
        /*
@@ -1592,19 +1789,26 @@ static int try_shell_exec(const char *cmd, char *const *argv)
                return 0;
        prog = path_lookup(interpr, 1);
        if (prog) {
+               int exec_id;
                int argc = 0;
-               const char **argv2;
+#ifndef _MSC_VER
+               const
+#endif
+               char **argv2;
                while (argv[argc]) argc++;
                ALLOC_ARRAY(argv2, argc + 1);
                argv2[0] = (char *)cmd; /* full path to the script file */
-               memcpy(&argv2[1], &argv[1], sizeof(*argv) * argc);
+               COPY_ARRAY(&argv2[1], &argv[1], argc);
+               exec_id = trace2_exec(prog, argv2);
                pid = mingw_spawnv(prog, argv2, 1);
                if (pid >= 0) {
                        int status;
                        if (waitpid(pid, &status, 0) < 0)
                                status = 255;
+                       trace2_exec_result(exec_id, status);
                        exit(status);
                }
+               trace2_exec_result(exec_id, -1);
                pid = 1;        /* indicate that we tried but failed */
                free(prog);
                free(argv2);
@@ -1617,12 +1821,17 @@ int mingw_execv(const char *cmd, char *const *argv)
        /* check if git_command is a shell script */
        if (!try_shell_exec(cmd, argv)) {
                int pid, status;
+               int exec_id;
 
+               exec_id = trace2_exec(cmd, (const char **)argv);
                pid = mingw_spawnv(cmd, (const char **)argv, 0);
-               if (pid < 0)
+               if (pid < 0) {
+                       trace2_exec_result(exec_id, -1);
                        return -1;
+               }
                if (waitpid(pid, &status, 0) < 0)
                        status = 255;
+               trace2_exec_result(exec_id, status);
                exit(status);
        }
        return -1;
@@ -1690,6 +1899,8 @@ char *mingw_getenv(const char *name)
        if (!w_key)
                die("Out of memory, (tried to allocate %u wchar_t's)", len_key);
        xutftowcs(w_key, name, len_key);
+       /* GetEnvironmentVariableW() only sets the last error upon failure */
+       SetLastError(ERROR_SUCCESS);
        len_value = GetEnvironmentVariableW(w_key, w_value, ARRAY_SIZE(w_value));
        if (!len_value && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
                free(w_key);
@@ -1746,142 +1957,10 @@ int mingw_putenv(const char *namevalue)
        return result ? 0 : -1;
 }
 
-/*
- * Note, this isn't a complete replacement for getaddrinfo. It assumes
- * that service contains a numerical port, or that it is null. It
- * does a simple search using gethostbyname, and returns one IPv4 host
- * if one was found.
- */
-static int WSAAPI getaddrinfo_stub(const char *node, const char *service,
-                                  const struct addrinfo *hints,
-                                  struct addrinfo **res)
-{
-       struct hostent *h = NULL;
-       struct addrinfo *ai;
-       struct sockaddr_in *sin;
-
-       if (node) {
-               h = gethostbyname(node);
-               if (!h)
-                       return WSAGetLastError();
-       }
-
-       ai = xmalloc(sizeof(struct addrinfo));
-       *res = ai;
-       ai->ai_flags = 0;
-       ai->ai_family = AF_INET;
-       ai->ai_socktype = hints ? hints->ai_socktype : 0;
-       switch (ai->ai_socktype) {
-       case SOCK_STREAM:
-               ai->ai_protocol = IPPROTO_TCP;
-               break;
-       case SOCK_DGRAM:
-               ai->ai_protocol = IPPROTO_UDP;
-               break;
-       default:
-               ai->ai_protocol = 0;
-               break;
-       }
-       ai->ai_addrlen = sizeof(struct sockaddr_in);
-       if (hints && (hints->ai_flags & AI_CANONNAME))
-               ai->ai_canonname = h ? xstrdup(h->h_name) : NULL;
-       else
-               ai->ai_canonname = NULL;
-
-       sin = xcalloc(1, ai->ai_addrlen);
-       sin->sin_family = AF_INET;
-       /* Note: getaddrinfo is supposed to allow service to be a string,
-        * which should be looked up using getservbyname. This is
-        * currently not implemented */
-       if (service)
-               sin->sin_port = htons(atoi(service));
-       if (h)
-               sin->sin_addr = *(struct in_addr *)h->h_addr;
-       else if (hints && (hints->ai_flags & AI_PASSIVE))
-               sin->sin_addr.s_addr = INADDR_ANY;
-       else
-               sin->sin_addr.s_addr = INADDR_LOOPBACK;
-       ai->ai_addr = (struct sockaddr *)sin;
-       ai->ai_next = NULL;
-       return 0;
-}
-
-static void WSAAPI freeaddrinfo_stub(struct addrinfo *res)
-{
-       free(res->ai_canonname);
-       free(res->ai_addr);
-       free(res);
-}
-
-static int WSAAPI getnameinfo_stub(const struct sockaddr *sa, socklen_t salen,
-                                  char *host, DWORD hostlen,
-                                  char *serv, DWORD servlen, int flags)
-{
-       const struct sockaddr_in *sin = (const struct sockaddr_in *)sa;
-       if (sa->sa_family != AF_INET)
-               return EAI_FAMILY;
-       if (!host && !serv)
-               return EAI_NONAME;
-
-       if (host && hostlen > 0) {
-               struct hostent *ent = NULL;
-               if (!(flags & NI_NUMERICHOST))
-                       ent = gethostbyaddr((const char *)&sin->sin_addr,
-                                           sizeof(sin->sin_addr), AF_INET);
-
-               if (ent)
-                       snprintf(host, hostlen, "%s", ent->h_name);
-               else if (flags & NI_NAMEREQD)
-                       return EAI_NONAME;
-               else
-                       snprintf(host, hostlen, "%s", inet_ntoa(sin->sin_addr));
-       }
-
-       if (serv && servlen > 0) {
-               struct servent *ent = NULL;
-               if (!(flags & NI_NUMERICSERV))
-                       ent = getservbyport(sin->sin_port,
-                                           flags & NI_DGRAM ? "udp" : "tcp");
-
-               if (ent)
-                       snprintf(serv, servlen, "%s", ent->s_name);
-               else
-                       snprintf(serv, servlen, "%d", ntohs(sin->sin_port));
-       }
-
-       return 0;
-}
-
-static HMODULE ipv6_dll = NULL;
-static void (WSAAPI *ipv6_freeaddrinfo)(struct addrinfo *res);
-static int (WSAAPI *ipv6_getaddrinfo)(const char *node, const char *service,
-                                     const struct addrinfo *hints,
-                                     struct addrinfo **res);
-static int (WSAAPI *ipv6_getnameinfo)(const struct sockaddr *sa, socklen_t salen,
-                                     char *host, DWORD hostlen,
-                                     char *serv, DWORD servlen, int flags);
-/*
- * gai_strerror is an inline function in the ws2tcpip.h header, so we
- * don't need to try to load that one dynamically.
- */
-
-static void socket_cleanup(void)
-{
-       WSACleanup();
-       if (ipv6_dll)
-               FreeLibrary(ipv6_dll);
-       ipv6_dll = NULL;
-       ipv6_freeaddrinfo = freeaddrinfo_stub;
-       ipv6_getaddrinfo = getaddrinfo_stub;
-       ipv6_getnameinfo = getnameinfo_stub;
-}
-
 static void ensure_socket_initialization(void)
 {
        WSADATA wsa;
        static int initialized = 0;
-       const char *libraries[] = { "ws2_32.dll", "wship6.dll", NULL };
-       const char **name;
 
        if (initialized)
                return;
@@ -1890,35 +1969,7 @@ static void ensure_socket_initialization(void)
                die("unable to initialize winsock subsystem, error %d",
                        WSAGetLastError());
 
-       for (name = libraries; *name; name++) {
-               ipv6_dll = LoadLibraryExA(*name, NULL,
-                                         LOAD_LIBRARY_SEARCH_SYSTEM32);
-               if (!ipv6_dll)
-                       continue;
-
-               ipv6_freeaddrinfo = (void (WSAAPI *)(struct addrinfo *))
-                       GetProcAddress(ipv6_dll, "freeaddrinfo");
-               ipv6_getaddrinfo = (int (WSAAPI *)(const char *, const char *,
-                                                  const struct addrinfo *,
-                                                  struct addrinfo **))
-                       GetProcAddress(ipv6_dll, "getaddrinfo");
-               ipv6_getnameinfo = (int (WSAAPI *)(const struct sockaddr *,
-                                                  socklen_t, char *, DWORD,
-                                                  char *, DWORD, int))
-                       GetProcAddress(ipv6_dll, "getnameinfo");
-               if (!ipv6_freeaddrinfo || !ipv6_getaddrinfo || !ipv6_getnameinfo) {
-                       FreeLibrary(ipv6_dll);
-                       ipv6_dll = NULL;
-               } else
-                       break;
-       }
-       if (!ipv6_freeaddrinfo || !ipv6_getaddrinfo || !ipv6_getnameinfo) {
-               ipv6_freeaddrinfo = freeaddrinfo_stub;
-               ipv6_getaddrinfo = getaddrinfo_stub;
-               ipv6_getnameinfo = getnameinfo_stub;
-       }
-
-       atexit(socket_cleanup);
+       atexit((void(*)(void)) WSACleanup);
        initialized = 1;
 }
 
@@ -1936,24 +1987,12 @@ struct hostent *mingw_gethostbyname(const char *host)
        return gethostbyname(host);
 }
 
-void mingw_freeaddrinfo(struct addrinfo *res)
-{
-       ipv6_freeaddrinfo(res);
-}
-
+#undef getaddrinfo
 int mingw_getaddrinfo(const char *node, const char *service,
                      const struct addrinfo *hints, struct addrinfo **res)
 {
        ensure_socket_initialization();
-       return ipv6_getaddrinfo(node, service, hints, res);
-}
-
-int mingw_getnameinfo(const struct sockaddr *sa, socklen_t salen,
-                     char *host, DWORD hostlen, char *serv, DWORD servlen,
-                     int flags)
-{
-       ensure_socket_initialization();
-       return ipv6_getnameinfo(sa, salen, host, hostlen, serv, servlen, flags);
+       return getaddrinfo(node, service, hints, res);
 }
 
 int mingw_socket(int domain, int type, int protocol)
@@ -2150,13 +2189,19 @@ struct passwd *getpwuid(int uid)
        static unsigned initialized;
        static char user_name[100];
        static struct passwd *p;
+       wchar_t buf[100];
        DWORD len;
 
        if (initialized)
                return p;
 
-       len = sizeof(user_name);
-       if (!GetUserName(user_name, &len)) {
+       len = ARRAY_SIZE(buf);
+       if (!GetUserNameW(buf, &len)) {
+               initialized = 1;
+               return NULL;
+       }
+
+       if (xwcstoutf(user_name, buf, sizeof(user_name)) < 0) {
                initialized = 1;
                return NULL;
        }
@@ -2320,8 +2365,33 @@ int mingw_raise(int sig)
                        sigint_fn(SIGINT);
                return 0;
 
+#if defined(_MSC_VER)
+       case SIGILL:
+       case SIGFPE:
+       case SIGSEGV:
+       case SIGTERM:
+       case SIGBREAK:
+       case SIGABRT:
+       case SIGABRT_COMPAT:
+               /*
+                * The <signal.h> header in the MS C Runtime defines 8 signals
+                * as being supported on the platform. Anything else causes an
+                * "Invalid signal or error" (which in DEBUG builds causes the
+                * Abort/Retry/Ignore dialog). We by-pass the CRT for things we
+                * already know will fail.
+                */
+               return raise(sig);
+       default:
+               errno = EINVAL;
+               return -1;
+
+#else
+
        default:
                return raise(sig);
+
+#endif
+
        }
 }
 
@@ -2503,16 +2573,42 @@ static void setup_windows_environment(void)
        /* simulate TERM to enable auto-color (see color.c) */
        if (!getenv("TERM"))
                setenv("TERM", "cygwin", 1);
+
+       /* calculate HOME if not set */
+       if (!getenv("HOME")) {
+               /*
+                * try $HOMEDRIVE$HOMEPATH - the home share may be a network
+                * location, thus also check if the path exists (i.e. is not
+                * disconnected)
+                */
+               if ((tmp = getenv("HOMEDRIVE"))) {
+                       struct strbuf buf = STRBUF_INIT;
+                       strbuf_addstr(&buf, tmp);
+                       if ((tmp = getenv("HOMEPATH"))) {
+                               strbuf_addstr(&buf, tmp);
+                               if (is_directory(buf.buf))
+                                       setenv("HOME", buf.buf, 1);
+                               else
+                                       tmp = NULL; /* use $USERPROFILE */
+                       }
+                       strbuf_release(&buf);
+               }
+               /* use $USERPROFILE if the home share is not available */
+               if (!tmp && (tmp = getenv("USERPROFILE")))
+                       setenv("HOME", tmp, 1);
+       }
 }
 
-int is_valid_win32_path(const char *path)
+int is_valid_win32_path(const char *path, int allow_literal_nul)
 {
+       const char *p = path;
        int preceding_space_or_period = 0, i = 0, periods = 0;
 
        if (!protect_ntfs)
                return 1;
 
        skip_dos_drive_prefix((char **)&path);
+       goto segment_start;
 
        for (;;) {
                char c = *(path++);
@@ -2527,7 +2623,85 @@ int is_valid_win32_path(const char *path)
                                return 1;
 
                        i = periods = preceding_space_or_period = 0;
-                       continue;
+
+segment_start:
+                       switch (*path) {
+                       case 'a': case 'A': /* AUX */
+                               if (((c = path[++i]) != 'u' && c != 'U') ||
+                                   ((c = path[++i]) != 'x' && c != 'X')) {
+not_a_reserved_name:
+                                       path += i;
+                                       continue;
+                               }
+                               break;
+                       case 'c': case 'C':
+                               /* COM1 ... COM9, CON, CONIN$, CONOUT$ */
+                               if ((c = path[++i]) != 'o' && c != 'O')
+                                       goto not_a_reserved_name;
+                               c = path[++i];
+                               if (c == 'm' || c == 'M') { /* COM1 ... COM9 */
+                                       c = path[++i];
+                                       if (c < '1' || c > '9')
+                                               goto not_a_reserved_name;
+                               } else if (c == 'n' || c == 'N') { /* CON */
+                                       c = path[i + 1];
+                                       if ((c == 'i' || c == 'I') &&
+                                           ((c = path[i + 2]) == 'n' ||
+                                            c == 'N') &&
+                                           path[i + 3] == '$')
+                                               i += 3; /* CONIN$ */
+                                       else if ((c == 'o' || c == 'O') &&
+                                                ((c = path[i + 2]) == 'u' ||
+                                                 c == 'U') &&
+                                                ((c = path[i + 3]) == 't' ||
+                                                 c == 'T') &&
+                                                path[i + 4] == '$')
+                                               i += 4; /* CONOUT$ */
+                               } else
+                                       goto not_a_reserved_name;
+                               break;
+                       case 'l': case 'L': /* LPT<N> */
+                               if (((c = path[++i]) != 'p' && c != 'P') ||
+                                   ((c = path[++i]) != 't' && c != 'T') ||
+                                   !isdigit(path[++i]))
+                                       goto not_a_reserved_name;
+                               break;
+                       case 'n': case 'N': /* NUL */
+                               if (((c = path[++i]) != 'u' && c != 'U') ||
+                                   ((c = path[++i]) != 'l' && c != 'L') ||
+                                   (allow_literal_nul &&
+                                    !path[i + 1] && p == path))
+                                       goto not_a_reserved_name;
+                               break;
+                       case 'p': case 'P': /* PRN */
+                               if (((c = path[++i]) != 'r' && c != 'R') ||
+                                   ((c = path[++i]) != 'n' && c != 'N'))
+                                       goto not_a_reserved_name;
+                               break;
+                       default:
+                               continue;
+                       }
+
+                       /*
+                        * So far, this looks like a reserved name. Let's see
+                        * whether it actually is one: trailing spaces, a file
+                        * extension, or an NTFS Alternate Data Stream do not
+                        * matter, the name is still reserved if any of those
+                        * follow immediately after the actual name.
+                        */
+                       i++;
+                       if (path[i] == ' ') {
+                               preceding_space_or_period = 1;
+                               while (path[++i] == ' ')
+                                       ; /* skip all spaces */
+                       }
+
+                       c = path[i];
+                       if (c && c != '.' && c != ':' && c != '/' && c != '\\')
+                               goto not_a_reserved_name;
+
+                       /* contains reserved name */
+                       return 0;
                case '.':
                        periods++;
                        /* fallthru */
@@ -2549,18 +2723,13 @@ int is_valid_win32_path(const char *path)
        }
 }
 
+#if !defined(_MSC_VER)
 /*
  * Disable MSVCRT command line wildcard expansion (__getmainargs called from
  * mingw startup code, see init.c in mingw runtime).
  */
 int _CRT_glob = 0;
-
-typedef struct {
-       int newmode;
-} _startupinfo;
-
-extern int __wgetmainargs(int *argc, wchar_t ***argv, wchar_t ***env, int glob,
-               _startupinfo *si);
+#endif
 
 static NORETURN void die_startup(void)
 {
@@ -2638,19 +2807,40 @@ static void maybe_redirect_std_handles(void)
                                  GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
 }
 
-void mingw_startup(void)
+#ifdef _MSC_VER
+#ifdef _DEBUG
+#include <crtdbg.h>
+#endif
+#endif
+
+/*
+ * We implement wmain() and compile with -municode, which would
+ * normally ignore main(), but we call the latter from the former
+ * so that we can handle non-ASCII command-line parameters
+ * appropriately.
+ *
+ * To be more compatible with the core git code, we convert
+ * argv into UTF8 and pass them directly to main().
+ */
+int wmain(int argc, const wchar_t **wargv)
 {
-       int i, maxlen, argc;
-       char *buffer;
-       wchar_t **wenv, **wargv;
-       _startupinfo si;
+       int i, maxlen, exit_status;
+       char *buffer, **save;
+       const char **argv;
 
-       maybe_redirect_std_handles();
+       trace2_initialize_clock();
 
-       /* get wide char arguments and environment */
-       si.newmode = 0;
-       if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)
-               die_startup();
+#ifdef _MSC_VER
+#ifdef _DEBUG
+       _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
+#endif
+
+#ifdef USE_MSVC_CRTDBG
+       _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
+#endif
+#endif
+
+       maybe_redirect_std_handles();
 
        /* determine size of argv and environ conversion buffer */
        maxlen = wcslen(wargv[0]);
@@ -2661,9 +2851,16 @@ void mingw_startup(void)
        maxlen = 3 * maxlen + 1;
        buffer = malloc_startup(maxlen);
 
-       /* convert command line arguments and environment to UTF-8 */
+       /*
+        * Create a UTF-8 version of w_argv. Also create a "save" copy
+        * to remember all the string pointers because parse_options()
+        * will remove claimed items from the argv that we pass down.
+        */
+       ALLOC_ARRAY(argv, argc + 1);
+       ALLOC_ARRAY(save, argc + 1);
        for (i = 0; i < argc; i++)
-               __argv[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen);
+               argv[i] = save[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen);
+       argv[i] = save[i] = NULL;
        free(buffer);
 
        /* fix Windows specific environment settings */
@@ -2682,6 +2879,16 @@ void mingw_startup(void)
 
        /* initialize Unicode console */
        winansi_init();
+
+       /* invoke the real main() using our utf8 version of argv. */
+       exit_status = main(argc, argv);
+
+       for (i = 0; i < argc; i++)
+               free(save[i]);
+       free(save);
+       free(argv);
+
+       return exit_status;
 }
 
 int uname(struct utsname *buf)