diff options
Diffstat (limited to 'compat/mingw.c')
-rw-r--r-- | compat/mingw.c | 741 |
1 files changed, 489 insertions, 252 deletions
diff --git a/compat/mingw.c b/compat/mingw.c index 8141f77189..d14065d60e 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -13,6 +13,19 @@ 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; + + argv_array_pushl(&cp.args, "mintty", "gdb", NULL); + argv_array_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; } @@ -363,6 +387,8 @@ static inline int needs_hiding(const char *path) /* ignore trailing slashes */ if (*path) basename = path; + else + break; } if (hide_dotfiles == HIDE_DOTFILES_TRUE) @@ -390,6 +416,12 @@ int mingw_mkdir(const char *path, int mode) { int ret; wchar_t wpath[MAX_PATH]; + + if (!is_valid_win32_path(path, 0)) { + errno = EINVAL; + return -1; + } + if (xutftowcs_path(wpath, path) < 0) return -1; ret = _wmkdir(wpath); @@ -463,7 +495,7 @@ int mingw_open (const char *filename, int oflags, ...) typedef int (*open_fn_t)(wchar_t const *wfilename, int oflags, ...); va_list args; unsigned mode; - int fd; + int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL); wchar_t wfilename[MAX_PATH]; open_fn_t open_fn; @@ -471,16 +503,21 @@ int mingw_open (const char *filename, int oflags, ...) mode = va_arg(args, int); va_end(args); - if (filename && !strcmp(filename, "/dev/null")) - filename = "nul"; + if (!is_valid_win32_path(filename, !create)) { + errno = create ? EINVAL : ENOENT; + return -1; + } 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) { @@ -538,10 +575,17 @@ FILE *mingw_fopen (const char *filename, const char *otype) FILE *file; wchar_t wfilename[MAX_PATH], wotype[4]; if (filename && !strcmp(filename, "/dev/null")) - filename = "nul"; - if (xutftowcs_path(wfilename, filename) < 0 || - xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0) + wcscpy(wfilename, L"nul"); + else if (!is_valid_win32_path(filename, 1)) { + int create = otype && strchr(otype, 'w'); + errno = create ? EINVAL : ENOENT; + return NULL; + } 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; @@ -560,10 +604,17 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream) FILE *file; wchar_t wfilename[MAX_PATH], wotype[4]; if (filename && !strcmp(filename, "/dev/null")) - filename = "nul"; - if (xutftowcs_path(wfilename, filename) < 0 || - xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0) + wcscpy(wfilename, L"nul"); + else if (!is_valid_win32_path(filename, 1)) { + int create = otype && strchr(otype, 'w'); + errno = create ? EINVAL : ENOENT; + return NULL; + } 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; @@ -984,16 +1035,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) @@ -1052,7 +1103,7 @@ static const char *quote_arg_msvc(const char *arg) p++; len++; } - if (*p == '"') + if (*p == '"' || !*p) n += count*2 + 1; continue; } @@ -1074,16 +1125,19 @@ static const char *quote_arg_msvc(const char *arg) count++; *d++ = *arg++; } - if (*arg == '"') { + if (*arg == '"' || !*arg) { while (count-- > 0) *d++ = '\\'; + /* don't escape the surrounding end quote */ + if (!*arg) + break; *d++ = '\\'; } } *d++ = *arg++; } *d++ = '"'; - *d++ = 0; + *d++ = '\0'; return q; } @@ -1096,13 +1150,14 @@ static const char *quote_arg_msys2(const char *arg) for (p = arg; *p; p++) { int ws = isspace(*p); - if (!ws && *p != '\\' && *p != '"' && *p != '{') + if (!ws && *p != '\\' && *p != '"' && *p != '{' && *p != '\'' && + *p != '?' && *p != '*' && *p != '~') continue; if (!buf.len) strbuf_addch(&buf, '"'); if (p != p2) strbuf_add(&buf, p2, p - p2); - if (!ws && *p != '{') + if (*p == '\\' || *p == '"') strbuf_addch(&buf, '\\'); p2 = p; } @@ -1112,7 +1167,7 @@ static const char *quote_arg_msys2(const char *arg) else if (!buf.len) return arg; else - strbuf_add(&buf, p2, p - p2), + strbuf_add(&buf, p2, p - p2); strbuf_addch(&buf, '"'); return strbuf_detach(&buf, 0); @@ -1161,14 +1216,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; } @@ -1183,7 +1245,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"); @@ -1229,11 +1291,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. @@ -1265,15 +1322,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); @@ -1296,7 +1353,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++) { @@ -1309,7 +1366,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'; @@ -1367,7 +1424,10 @@ static inline int match_last_path_component(const char *path, size_t *len, static int is_msys2_sh(const char *cmd) { - if (cmd && !strcmp(cmd, "sh")) { + if (!cmd) + return 0; + + if (!strcmp(cmd, "sh")) { static int ret = -1; char *p; @@ -1387,6 +1447,16 @@ static int is_msys2_sh(const char *cmd) } return ret; } + + if (ends_with(cmd, "\\sh.exe")) { + static char *sh; + + if (!sh) + sh = path_lookup("sh", 0); + + return !fspathcmp(cmd, sh); + } + return 0; } @@ -1394,20 +1464,39 @@ 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; BOOL ret; HANDLE cons; const char *(*quote_arg)(const char *arg) = - is_msys2_sh(*argv) ? quote_arg_msys2 : quote_arg_msvc; + is_msys2_sh(cmd ? cmd : *argv) ? + quote_arg_msys2 : quote_arg_msvc; + + /* 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) { @@ -1431,13 +1520,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; @@ -1466,16 +1569,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); /* @@ -1551,19 +1736,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); @@ -1576,12 +1768,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; @@ -1649,6 +1846,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); @@ -1705,142 +1904,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; @@ -1849,35 +1916,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; } @@ -1895,24 +1934,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) @@ -2109,13 +2136,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; } @@ -2279,8 +2312,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 + } } @@ -2462,20 +2520,161 @@ 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 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++); + switch (c) { + case '\0': + case '/': case '\\': + /* cannot end in ` ` or `.`, except for `.` and `..` */ + if (preceding_space_or_period && + (i != periods || periods > 2)) + return 0; + if (!c) + return 1; + + i = periods = preceding_space_or_period = 0; + +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': /* COM<N>, CON, CONIN$, CONOUT$ */ + if ((c = path[++i]) != 'o' && c != 'O') + goto not_a_reserved_name; + c = path[++i]; + if (c == 'm' || c == 'M') { /* COM<N> */ + if (!isdigit(path[++i])) + 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 */ + case ' ': + preceding_space_or_period = 1; + i++; + continue; + case ':': /* DOS drive prefix was already skipped */ + case '<': case '>': case '"': case '|': case '?': case '*': + /* illegal character */ + return 0; + default: + if (c > '\0' && c < '\x20') + /* illegal character */ + return 0; + } + preceding_space_or_period = 0; + i++; + } +} + +#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) { @@ -2553,19 +2752,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]); @@ -2576,9 +2796,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 */ @@ -2597,6 +2824,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) |