2007-12-01 20:24:59 +00:00
|
|
|
#include "../git-compat-util.h"
|
2008-09-27 08:43:01 +00:00
|
|
|
#include "win32.h"
|
2022-03-02 10:06:24 +00:00
|
|
|
#include <aclapi.h>
|
2009-06-01 06:41:45 +00:00
|
|
|
#include <conio.h>
|
2012-03-15 17:21:28 +00:00
|
|
|
#include <wchar.h>
|
2007-11-24 21:49:16 +00:00
|
|
|
#include "../strbuf.h"
|
2011-02-07 20:51:21 +00:00
|
|
|
#include "../run-command.h"
|
2014-07-17 15:37:55 +00:00
|
|
|
#include "../cache.h"
|
2018-10-15 09:47:06 +00:00
|
|
|
#include "win32/lazyload.h"
|
2018-10-30 18:40:06 +00:00
|
|
|
#include "../config.h"
|
mingw: special-case arguments to `sh`
The MSYS2 runtime does its best to emulate the command-line wildcard
expansion and de-quoting which would be performed by the calling Unix
shell on Unix systems.
Those Unix shell quoting rules differ from the quoting rules applying to
Windows' cmd and Powershell, making it a little awkward to quote
command-line parameters properly when spawning other processes.
In particular, git.exe passes arguments to subprocesses that are *not*
intended to be interpreted as wildcards, and if they contain
backslashes, those are not to be interpreted as escape characters, e.g.
when passing Windows paths.
Note: this is only a problem when calling MSYS2 executables, not when
calling MINGW executables such as git.exe. However, we do call MSYS2
executables frequently, most notably when setting the use_shell flag in
the child_process structure.
There is no elegant way to determine whether the .exe file to be
executed is an MSYS2 program or a MINGW one. But since the use case of
passing a command line through the shell is so prevalent, we need to
work around this issue at least when executing sh.exe.
Let's introduce an ugly, hard-coded test whether argv[0] is "sh", and
whether it refers to the MSYS2 Bash, to determine whether we need to
quote the arguments differently than usual.
That still does not fix the issue completely, but at least it is
something.
Incidentally, this also fixes the problem where `git clone \\server\repo`
failed due to incorrect handling of the backslashes when handing the path
to the git-upload-pack process.
Further, we need to take care to quote not only whitespace and
backslashes, but also curly brackets. As aliases frequently go through
the MSYS2 Bash, and as aliases frequently get parameters such as
HEAD@{yesterday}, this is really important. As an early version of this
patch broke this, let's make sure that this does not regress by adding a
test case for that.
Helped-by: Kim Gybels <kgybels@infogroep.be>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-01-17 20:14:48 +00:00
|
|
|
#include "dir.h"
|
2022-01-08 16:02:30 +00:00
|
|
|
#define SECURITY_WIN32
|
|
|
|
#include <sspi.h>
|
2007-12-01 20:24:59 +00:00
|
|
|
|
2016-01-15 13:24:34 +00:00
|
|
|
#define HCAST(type, handle) ((type)(intptr_t)handle)
|
|
|
|
|
2011-02-07 20:50:26 +00:00
|
|
|
static const int delay[] = { 0, 1, 10, 20, 40 };
|
|
|
|
|
2020-02-13 21:49:53 +00:00
|
|
|
void open_in_gdb(void)
|
|
|
|
{
|
|
|
|
static struct child_process cp = CHILD_PROCESS_INIT;
|
|
|
|
extern char *_pgmptr;
|
|
|
|
|
2020-07-28 20:24:53 +00:00
|
|
|
strvec_pushl(&cp.args, "mintty", "gdb", NULL);
|
|
|
|
strvec_pushf(&cp.args, "--pid=%d", getpid());
|
2020-02-13 21:49:53 +00:00
|
|
|
cp.clean_on_exit = 1;
|
|
|
|
if (start_command(&cp) < 0)
|
|
|
|
die_errno("Could not start gdb");
|
|
|
|
sleep(1);
|
|
|
|
}
|
|
|
|
|
2010-01-15 20:12:20 +00:00
|
|
|
int err_win_to_posix(DWORD winerr)
|
2009-01-24 14:04:39 +00:00
|
|
|
{
|
|
|
|
int error = ENOSYS;
|
|
|
|
switch(winerr) {
|
|
|
|
case ERROR_ACCESS_DENIED: error = EACCES; break;
|
|
|
|
case ERROR_ACCOUNT_DISABLED: error = EACCES; break;
|
|
|
|
case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break;
|
|
|
|
case ERROR_ALREADY_ASSIGNED: error = EBUSY; break;
|
|
|
|
case ERROR_ALREADY_EXISTS: error = EEXIST; break;
|
|
|
|
case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break;
|
|
|
|
case ERROR_BAD_COMMAND: error = EIO; break;
|
|
|
|
case ERROR_BAD_DEVICE: error = ENODEV; break;
|
|
|
|
case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break;
|
|
|
|
case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break;
|
|
|
|
case ERROR_BAD_FORMAT: error = ENOEXEC; break;
|
|
|
|
case ERROR_BAD_LENGTH: error = EINVAL; break;
|
|
|
|
case ERROR_BAD_PATHNAME: error = ENOENT; break;
|
|
|
|
case ERROR_BAD_PIPE: error = EPIPE; break;
|
|
|
|
case ERROR_BAD_UNIT: error = ENODEV; break;
|
|
|
|
case ERROR_BAD_USERNAME: error = EINVAL; break;
|
|
|
|
case ERROR_BROKEN_PIPE: error = EPIPE; break;
|
|
|
|
case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break;
|
|
|
|
case ERROR_BUSY: error = EBUSY; break;
|
|
|
|
case ERROR_BUSY_DRIVE: error = EBUSY; break;
|
|
|
|
case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break;
|
|
|
|
case ERROR_CANNOT_MAKE: error = EACCES; break;
|
|
|
|
case ERROR_CANTOPEN: error = EIO; break;
|
|
|
|
case ERROR_CANTREAD: error = EIO; break;
|
|
|
|
case ERROR_CANTWRITE: error = EIO; break;
|
|
|
|
case ERROR_CRC: error = EIO; break;
|
|
|
|
case ERROR_CURRENT_DIRECTORY: error = EACCES; break;
|
|
|
|
case ERROR_DEVICE_IN_USE: error = EBUSY; break;
|
|
|
|
case ERROR_DEV_NOT_EXIST: error = ENODEV; break;
|
|
|
|
case ERROR_DIRECTORY: error = EINVAL; break;
|
|
|
|
case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break;
|
|
|
|
case ERROR_DISK_CHANGE: error = EIO; break;
|
|
|
|
case ERROR_DISK_FULL: error = ENOSPC; break;
|
|
|
|
case ERROR_DRIVE_LOCKED: error = EBUSY; break;
|
|
|
|
case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break;
|
|
|
|
case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break;
|
|
|
|
case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break;
|
|
|
|
case ERROR_FILE_EXISTS: error = EEXIST; break;
|
|
|
|
case ERROR_FILE_INVALID: error = ENODEV; break;
|
|
|
|
case ERROR_FILE_NOT_FOUND: error = ENOENT; break;
|
|
|
|
case ERROR_GEN_FAILURE: error = EIO; break;
|
|
|
|
case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break;
|
|
|
|
case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break;
|
|
|
|
case ERROR_INVALID_ACCESS: error = EACCES; break;
|
|
|
|
case ERROR_INVALID_ADDRESS: error = EFAULT; break;
|
|
|
|
case ERROR_INVALID_BLOCK: error = EFAULT; break;
|
|
|
|
case ERROR_INVALID_DATA: error = EINVAL; break;
|
|
|
|
case ERROR_INVALID_DRIVE: error = ENODEV; break;
|
|
|
|
case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break;
|
|
|
|
case ERROR_INVALID_FLAGS: error = EINVAL; break;
|
|
|
|
case ERROR_INVALID_FUNCTION: error = ENOSYS; break;
|
|
|
|
case ERROR_INVALID_HANDLE: error = EBADF; break;
|
|
|
|
case ERROR_INVALID_LOGON_HOURS: error = EACCES; break;
|
|
|
|
case ERROR_INVALID_NAME: error = EINVAL; break;
|
|
|
|
case ERROR_INVALID_OWNER: error = EINVAL; break;
|
|
|
|
case ERROR_INVALID_PARAMETER: error = EINVAL; break;
|
|
|
|
case ERROR_INVALID_PASSWORD: error = EPERM; break;
|
|
|
|
case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break;
|
|
|
|
case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break;
|
|
|
|
case ERROR_INVALID_TARGET_HANDLE: error = EIO; break;
|
|
|
|
case ERROR_INVALID_WORKSTATION: error = EACCES; break;
|
|
|
|
case ERROR_IO_DEVICE: error = EIO; break;
|
|
|
|
case ERROR_IO_INCOMPLETE: error = EINTR; break;
|
|
|
|
case ERROR_LOCKED: error = EBUSY; break;
|
|
|
|
case ERROR_LOCK_VIOLATION: error = EACCES; break;
|
|
|
|
case ERROR_LOGON_FAILURE: error = EACCES; break;
|
|
|
|
case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break;
|
|
|
|
case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break;
|
|
|
|
case ERROR_MORE_DATA: error = EPIPE; break;
|
|
|
|
case ERROR_NEGATIVE_SEEK: error = ESPIPE; break;
|
|
|
|
case ERROR_NOACCESS: error = EFAULT; break;
|
|
|
|
case ERROR_NONE_MAPPED: error = EINVAL; break;
|
|
|
|
case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break;
|
|
|
|
case ERROR_NOT_READY: error = EAGAIN; break;
|
|
|
|
case ERROR_NOT_SAME_DEVICE: error = EXDEV; break;
|
|
|
|
case ERROR_NO_DATA: error = EPIPE; break;
|
|
|
|
case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break;
|
|
|
|
case ERROR_NO_PROC_SLOTS: error = EAGAIN; break;
|
|
|
|
case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break;
|
|
|
|
case ERROR_OPEN_FAILED: error = EIO; break;
|
|
|
|
case ERROR_OPEN_FILES: error = EBUSY; break;
|
|
|
|
case ERROR_OPERATION_ABORTED: error = EINTR; break;
|
|
|
|
case ERROR_OUTOFMEMORY: error = ENOMEM; break;
|
|
|
|
case ERROR_PASSWORD_EXPIRED: error = EACCES; break;
|
|
|
|
case ERROR_PATH_BUSY: error = EBUSY; break;
|
|
|
|
case ERROR_PATH_NOT_FOUND: error = ENOENT; break;
|
|
|
|
case ERROR_PIPE_BUSY: error = EBUSY; break;
|
|
|
|
case ERROR_PIPE_CONNECTED: error = EPIPE; break;
|
|
|
|
case ERROR_PIPE_LISTENING: error = EPIPE; break;
|
|
|
|
case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break;
|
|
|
|
case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break;
|
|
|
|
case ERROR_READ_FAULT: error = EIO; break;
|
|
|
|
case ERROR_SEEK: error = EIO; break;
|
|
|
|
case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break;
|
|
|
|
case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break;
|
|
|
|
case ERROR_SHARING_VIOLATION: error = EACCES; break;
|
|
|
|
case ERROR_STACK_OVERFLOW: error = ENOMEM; break;
|
2019-12-02 11:33:29 +00:00
|
|
|
case ERROR_SUCCESS: BUG("err_win_to_posix() called without an error!");
|
2009-01-24 14:04:39 +00:00
|
|
|
case ERROR_SWAPERROR: error = ENOENT; break;
|
|
|
|
case ERROR_TOO_MANY_MODULES: error = EMFILE; break;
|
|
|
|
case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break;
|
|
|
|
case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break;
|
|
|
|
case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break;
|
|
|
|
case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break;
|
|
|
|
case ERROR_WRITE_FAULT: error = EIO; break;
|
|
|
|
case ERROR_WRITE_PROTECT: error = EROFS; break;
|
|
|
|
}
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2011-02-07 20:50:26 +00:00
|
|
|
static inline int is_file_in_use_error(DWORD errcode)
|
|
|
|
{
|
|
|
|
switch (errcode) {
|
|
|
|
case ERROR_SHARING_VIOLATION:
|
|
|
|
case ERROR_ACCESS_DENIED:
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-07 20:51:21 +00:00
|
|
|
static int read_yes_no_answer(void)
|
|
|
|
{
|
|
|
|
char answer[1024];
|
|
|
|
|
|
|
|
if (fgets(answer, sizeof(answer), stdin)) {
|
|
|
|
size_t answer_len = strlen(answer);
|
|
|
|
int got_full_line = 0, c;
|
|
|
|
|
|
|
|
/* remove the newline */
|
|
|
|
if (answer_len >= 2 && answer[answer_len-2] == '\r') {
|
|
|
|
answer[answer_len-2] = '\0';
|
|
|
|
got_full_line = 1;
|
|
|
|
} else if (answer_len >= 1 && answer[answer_len-1] == '\n') {
|
|
|
|
answer[answer_len-1] = '\0';
|
|
|
|
got_full_line = 1;
|
|
|
|
}
|
|
|
|
/* flush the buffer in case we did not get the full line */
|
|
|
|
if (!got_full_line)
|
|
|
|
while ((c = getchar()) != EOF && c != '\n')
|
|
|
|
;
|
|
|
|
} else
|
|
|
|
/* we could not read, return the
|
|
|
|
* default answer which is no */
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (tolower(answer[0]) == 'y' && !answer[1])
|
|
|
|
return 1;
|
|
|
|
if (!strncasecmp(answer, "yes", sizeof(answer)))
|
|
|
|
return 1;
|
|
|
|
if (tolower(answer[0]) == 'n' && !answer[1])
|
|
|
|
return 0;
|
|
|
|
if (!strncasecmp(answer, "no", sizeof(answer)))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* did not find an answer we understand */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ask_yes_no_if_possible(const char *format, ...)
|
|
|
|
{
|
|
|
|
char question[4096];
|
|
|
|
const char *retry_hook[] = { NULL, NULL, NULL };
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, format);
|
|
|
|
vsnprintf(question, sizeof(question), format, args);
|
|
|
|
va_end(args);
|
|
|
|
|
2011-06-06 07:06:02 +00:00
|
|
|
if ((retry_hook[0] = mingw_getenv("GIT_ASK_YESNO"))) {
|
2011-02-07 20:51:21 +00:00
|
|
|
retry_hook[1] = question;
|
|
|
|
return !run_command_v_opt(retry_hook, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!isatty(_fileno(stdin)) || !isatty(_fileno(stderr)))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
int answer;
|
|
|
|
fprintf(stderr, "%s (y/n) ", question);
|
|
|
|
|
|
|
|
if ((answer = read_yes_no_answer()) >= 0)
|
|
|
|
return answer;
|
|
|
|
|
|
|
|
fprintf(stderr, "Sorry, I did not understand your answer. "
|
|
|
|
"Please type 'y' or 'n'\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-30 18:40:06 +00:00
|
|
|
/* Windows only */
|
|
|
|
enum hide_dotfiles_type {
|
|
|
|
HIDE_DOTFILES_FALSE = 0,
|
|
|
|
HIDE_DOTFILES_TRUE,
|
|
|
|
HIDE_DOTFILES_DOTGITONLY
|
|
|
|
};
|
|
|
|
|
2019-11-22 14:41:05 +00:00
|
|
|
static int core_restrict_inherited_handles = -1;
|
2018-10-30 18:40:06 +00:00
|
|
|
static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
|
2018-10-30 18:40:07 +00:00
|
|
|
static char *unset_environment_variables;
|
2018-10-30 18:40:06 +00:00
|
|
|
|
2018-10-30 18:40:04 +00:00
|
|
|
int mingw_core_config(const char *var, const char *value, void *cb)
|
|
|
|
{
|
2018-10-30 18:40:06 +00:00
|
|
|
if (!strcmp(var, "core.hidedotfiles")) {
|
|
|
|
if (value && !strcasecmp(value, "dotgitonly"))
|
|
|
|
hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
|
|
|
|
else
|
|
|
|
hide_dotfiles = git_config_bool(var, value);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-10-30 18:40:07 +00:00
|
|
|
if (!strcmp(var, "core.unsetenvvars")) {
|
|
|
|
free(unset_environment_variables);
|
|
|
|
unset_environment_variables = xstrdup(value);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-22 14:41:05 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-10-30 18:40:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-10-23 10:52:49 +00:00
|
|
|
/* Normalizes NT paths as returned by some low-level APIs. */
|
|
|
|
static wchar_t *normalize_ntpath(wchar_t *wbuf)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
/* fix absolute path prefixes */
|
|
|
|
if (wbuf[0] == '\\') {
|
|
|
|
/* strip NT namespace prefixes */
|
|
|
|
if (!wcsncmp(wbuf, L"\\??\\", 4) ||
|
|
|
|
!wcsncmp(wbuf, L"\\\\?\\", 4))
|
|
|
|
wbuf += 4;
|
|
|
|
else if (!wcsnicmp(wbuf, L"\\DosDevices\\", 12))
|
|
|
|
wbuf += 12;
|
|
|
|
/* replace remaining '...UNC\' with '\\' */
|
|
|
|
if (!wcsnicmp(wbuf, L"UNC\\", 4)) {
|
|
|
|
wbuf += 2;
|
|
|
|
*wbuf = '\\';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* convert backslashes to slashes */
|
|
|
|
for (i = 0; wbuf[i]; i++)
|
|
|
|
if (wbuf[i] == '\\')
|
|
|
|
wbuf[i] = '/';
|
|
|
|
return wbuf;
|
|
|
|
}
|
|
|
|
|
2011-02-07 20:49:33 +00:00
|
|
|
int mingw_unlink(const char *pathname)
|
|
|
|
{
|
2011-02-07 20:50:26 +00:00
|
|
|
int ret, tries = 0;
|
2012-03-15 17:21:28 +00:00
|
|
|
wchar_t wpathname[MAX_PATH];
|
|
|
|
if (xutftowcs_path(wpathname, pathname) < 0)
|
|
|
|
return -1;
|
2011-02-07 20:50:26 +00:00
|
|
|
|
2020-08-17 10:37:02 +00:00
|
|
|
if (DeleteFileW(wpathname))
|
|
|
|
return 0;
|
|
|
|
|
2011-02-07 20:49:33 +00:00
|
|
|
/* read-only files cannot be removed */
|
2012-03-15 17:21:28 +00:00
|
|
|
_wchmod(wpathname, 0666);
|
|
|
|
while ((ret = _wunlink(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
|
2011-02-07 20:50:26 +00:00
|
|
|
if (!is_file_in_use_error(GetLastError()))
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
* We assume that some other process had the source or
|
|
|
|
* destination file open at the wrong moment and retry.
|
|
|
|
* In order to give the other process a higher chance to
|
|
|
|
* complete its operation, we give up our time slice now.
|
|
|
|
* If we have to retry again, we do sleep a bit.
|
|
|
|
*/
|
|
|
|
Sleep(delay[tries]);
|
|
|
|
tries++;
|
|
|
|
}
|
2011-02-07 20:51:21 +00:00
|
|
|
while (ret == -1 && is_file_in_use_error(GetLastError()) &&
|
|
|
|
ask_yes_no_if_possible("Unlink of file '%s' failed. "
|
|
|
|
"Should I try again?", pathname))
|
2012-03-15 17:21:28 +00:00
|
|
|
ret = _wunlink(wpathname);
|
2011-02-07 20:50:26 +00:00
|
|
|
return ret;
|
2011-02-07 20:49:33 +00:00
|
|
|
}
|
|
|
|
|
2012-03-15 17:21:28 +00:00
|
|
|
static int is_dir_empty(const wchar_t *wpath)
|
2011-02-07 20:54:01 +00:00
|
|
|
{
|
2012-03-15 17:21:28 +00:00
|
|
|
WIN32_FIND_DATAW findbuf;
|
2011-02-07 20:54:01 +00:00
|
|
|
HANDLE handle;
|
2012-03-15 17:21:28 +00:00
|
|
|
wchar_t wbuf[MAX_PATH + 2];
|
|
|
|
wcscpy(wbuf, wpath);
|
|
|
|
wcscat(wbuf, L"\\*");
|
|
|
|
handle = FindFirstFileW(wbuf, &findbuf);
|
|
|
|
if (handle == INVALID_HANDLE_VALUE)
|
2011-02-07 20:54:01 +00:00
|
|
|
return GetLastError() == ERROR_NO_MORE_FILES;
|
|
|
|
|
2012-03-15 17:21:28 +00:00
|
|
|
while (!wcscmp(findbuf.cFileName, L".") ||
|
|
|
|
!wcscmp(findbuf.cFileName, L".."))
|
|
|
|
if (!FindNextFileW(handle, &findbuf)) {
|
|
|
|
DWORD err = GetLastError();
|
|
|
|
FindClose(handle);
|
|
|
|
return err == ERROR_NO_MORE_FILES;
|
2011-02-07 20:54:01 +00:00
|
|
|
}
|
|
|
|
FindClose(handle);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-02-07 20:52:34 +00:00
|
|
|
int mingw_rmdir(const char *pathname)
|
|
|
|
{
|
|
|
|
int ret, tries = 0;
|
2012-03-15 17:21:28 +00:00
|
|
|
wchar_t wpathname[MAX_PATH];
|
2021-08-02 21:07:30 +00:00
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Contrary to Linux' `rmdir()`, Windows' _wrmdir() and _rmdir()
|
|
|
|
* (and `RemoveDirectoryW()`) will attempt to remove the target of a
|
|
|
|
* symbolic link (if it points to a directory).
|
|
|
|
*
|
|
|
|
* This behavior breaks the assumption of e.g. `remove_path()` which
|
|
|
|
* upon successful deletion of a file will attempt to remove its parent
|
|
|
|
* directories recursively until failure (which usually happens when
|
|
|
|
* the directory is not empty).
|
|
|
|
*
|
|
|
|
* Therefore, before calling `_wrmdir()`, we first check if the path is
|
|
|
|
* a symbolic link. If it is, we exit and return the same error as
|
|
|
|
* Linux' `rmdir()` would, i.e. `ENOTDIR`.
|
|
|
|
*/
|
|
|
|
if (!mingw_lstat(pathname, &st) && S_ISLNK(st.st_mode)) {
|
|
|
|
errno = ENOTDIR;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-03-15 17:21:28 +00:00
|
|
|
if (xutftowcs_path(wpathname, pathname) < 0)
|
|
|
|
return -1;
|
2011-02-07 20:52:34 +00:00
|
|
|
|
2012-03-15 17:21:28 +00:00
|
|
|
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
|
2011-02-07 20:52:34 +00:00
|
|
|
if (!is_file_in_use_error(GetLastError()))
|
2012-12-10 14:42:27 +00:00
|
|
|
errno = err_win_to_posix(GetLastError());
|
|
|
|
if (errno != EACCES)
|
2011-02-07 20:52:34 +00:00
|
|
|
break;
|
2012-03-15 17:21:28 +00:00
|
|
|
if (!is_dir_empty(wpathname)) {
|
2011-02-07 20:54:01 +00:00
|
|
|
errno = ENOTEMPTY;
|
|
|
|
break;
|
|
|
|
}
|
2011-02-07 20:52:34 +00:00
|
|
|
/*
|
|
|
|
* We assume that some other process had the source or
|
|
|
|
* destination file open at the wrong moment and retry.
|
|
|
|
* In order to give the other process a higher chance to
|
|
|
|
* complete its operation, we give up our time slice now.
|
|
|
|
* If we have to retry again, we do sleep a bit.
|
|
|
|
*/
|
|
|
|
Sleep(delay[tries]);
|
|
|
|
tries++;
|
|
|
|
}
|
2012-12-10 14:42:27 +00:00
|
|
|
while (ret == -1 && errno == EACCES && is_file_in_use_error(GetLastError()) &&
|
2011-02-07 20:52:34 +00:00
|
|
|
ask_yes_no_if_possible("Deletion of directory '%s' failed. "
|
|
|
|
"Should I try again?", pathname))
|
2012-03-15 17:21:28 +00:00
|
|
|
ret = _wrmdir(wpathname);
|
checkout: fix bug that makes checkout follow symlinks in leading path
Before checking out a file, we have to confirm that all of its leading
components are real existing directories. And to reduce the number of
lstat() calls in this process, we cache the last leading path known to
contain only directories. However, when a path collision occurs (e.g.
when checking out case-sensitive files in case-insensitive file
systems), a cached path might have its file type changed on disk,
leaving the cache on an invalid state. Normally, this doesn't bring
any bad consequences as we usually check out files in index order, and
therefore, by the time the cached path becomes outdated, we no longer
need it anyway (because all files in that directory would have already
been written).
But, there are some users of the checkout machinery that do not always
follow the index order. In particular: checkout-index writes the paths
in the same order that they appear on the CLI (or stdin); and the
delayed checkout feature -- used when a long-running filter process
replies with "status=delayed" -- postpones the checkout of some entries,
thus modifying the checkout order.
When we have to check out an out-of-order entry and the lstat() cache is
invalid (due to a previous path collision), checkout_entry() may end up
using the invalid data and thrusting that the leading components are
real directories when, in reality, they are not. In the best case
scenario, where the directory was replaced by a regular file, the user
will get an error: "fatal: unable to create file 'foo/bar': Not a
directory". But if the directory was replaced by a symlink, checkout
could actually end up following the symlink and writing the file at a
wrong place, even outside the repository. Since delayed checkout is
affected by this bug, it could be used by an attacker to write
arbitrary files during the clone of a maliciously crafted repository.
Some candidate solutions considered were to disable the lstat() cache
during unordered checkouts or sort the entries before passing them to
the checkout machinery. But both ideas include some performance penalty
and they don't future-proof the code against new unordered use cases.
Instead, we now manually reset the lstat cache whenever we successfully
remove a directory. Note: We are not even checking whether the directory
was the same as the lstat cache points to because we might face a
scenario where the paths refer to the same location but differ due to
case folding, precomposed UTF-8 issues, or the presence of `..`
components in the path. Two regression tests, with case-collisions and
utf8-collisions, are also added for both checkout-index and delayed
checkout.
Note: to make the previously mentioned clone attack unfeasible, it would
be sufficient to reset the lstat cache only after the remove_subtree()
call inside checkout_entry(). This is the place where we would remove a
directory whose path collides with the path of another entry that we are
currently trying to check out (possibly a symlink). However, in the
interest of a thorough fix that does not leave Git open to
similar-but-not-identical attack vectors, we decided to intercept
all `rmdir()` calls in one fell swoop.
This addresses CVE-2021-21300.
Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Matheus Tavares <matheus.bernardino@usp.br>
2020-12-10 13:27:55 +00:00
|
|
|
if (!ret)
|
|
|
|
invalidate_lstat_cache();
|
2012-03-15 17:21:28 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
mingw: introduce the 'core.hideDotFiles' setting
On Unix (and Linux), files and directories whose names start with a dot
are usually not shown by default. This convention is used by Git: the
.git/ directory should be left alone by regular users, and only accessed
through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden by default, as e.g. Eclipse does not
show them (and the user would therefore be unable to see, say, a
.gitattributes file).
In over five years since the last attempt to bring this patch into core
Git, a slightly buggy version of this patch has served Git for Windows'
users well: no single report indicated problems with the hidden .git/
directory, and the stream of problems caused by the previously non-hidden
.git/ directory simply stopped. The bugs have been fixed during the
process of getting this patch upstream.
Note that there is a funny quirk we have to pay attention to when
creating hidden files: we use Win32's _wopen() function which
transmogrifies its arguments and hands off to Win32's CreateFile()
function. That latter function errors out with ERROR_ACCESS_DENIED (the
equivalent of EACCES) when the equivalent of the O_CREAT flag was passed
and the file attributes (including the hidden flag) do not match an
existing file's. And _wopen() accepts no parameter that would be
transmogrified into said hidden flag. Therefore, we simply try again
without O_CREAT.
A slightly different method is required for our fopen()/freopen()
function as we cannot even *remove* the implicit O_CREAT flag.
Therefore, we briefly mark existing files as unhidden when opening them
via fopen()/freopen().
The ERROR_ACCESS_DENIED error can also be triggered by opening a file
that is marked as a system file (which is unlikely to be tracked in
Git), and by trying to create a file that has *just* been deleted and is
awaiting the last open handles to be released (which would be handled
better by the "Try again?" logic, a story for a different patch series,
though). In both cases, it does not matter much if we try again without
the O_CREAT flag, read: it does not hurt, either.
For details how ERROR_ACCESS_DENIED can be triggered, see
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858
Original-patch-by: Erik Faye-Lund <kusmabite@gmail.com>
Initial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-05-11 08:43:37 +00:00
|
|
|
static inline int needs_hiding(const char *path)
|
|
|
|
{
|
|
|
|
const char *basename;
|
|
|
|
|
|
|
|
if (hide_dotfiles == HIDE_DOTFILES_FALSE)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* We cannot use basename(), as it would remove trailing slashes */
|
2018-12-15 04:33:30 +00:00
|
|
|
win32_skip_dos_drive_prefix((char **)&path);
|
mingw: introduce the 'core.hideDotFiles' setting
On Unix (and Linux), files and directories whose names start with a dot
are usually not shown by default. This convention is used by Git: the
.git/ directory should be left alone by regular users, and only accessed
through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden by default, as e.g. Eclipse does not
show them (and the user would therefore be unable to see, say, a
.gitattributes file).
In over five years since the last attempt to bring this patch into core
Git, a slightly buggy version of this patch has served Git for Windows'
users well: no single report indicated problems with the hidden .git/
directory, and the stream of problems caused by the previously non-hidden
.git/ directory simply stopped. The bugs have been fixed during the
process of getting this patch upstream.
Note that there is a funny quirk we have to pay attention to when
creating hidden files: we use Win32's _wopen() function which
transmogrifies its arguments and hands off to Win32's CreateFile()
function. That latter function errors out with ERROR_ACCESS_DENIED (the
equivalent of EACCES) when the equivalent of the O_CREAT flag was passed
and the file attributes (including the hidden flag) do not match an
existing file's. And _wopen() accepts no parameter that would be
transmogrified into said hidden flag. Therefore, we simply try again
without O_CREAT.
A slightly different method is required for our fopen()/freopen()
function as we cannot even *remove* the implicit O_CREAT flag.
Therefore, we briefly mark existing files as unhidden when opening them
via fopen()/freopen().
The ERROR_ACCESS_DENIED error can also be triggered by opening a file
that is marked as a system file (which is unlikely to be tracked in
Git), and by trying to create a file that has *just* been deleted and is
awaiting the last open handles to be released (which would be handled
better by the "Try again?" logic, a story for a different patch series,
though). In both cases, it does not matter much if we try again without
the O_CREAT flag, read: it does not hurt, either.
For details how ERROR_ACCESS_DENIED can be triggered, see
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858
Original-patch-by: Erik Faye-Lund <kusmabite@gmail.com>
Initial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-05-11 08:43:37 +00:00
|
|
|
if (!*path)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (basename = path; *path; path++)
|
|
|
|
if (is_dir_sep(*path)) {
|
|
|
|
do {
|
|
|
|
path++;
|
|
|
|
} while (is_dir_sep(*path));
|
|
|
|
/* ignore trailing slashes */
|
|
|
|
if (*path)
|
|
|
|
basename = path;
|
2019-10-25 14:13:36 +00:00
|
|
|
else
|
|
|
|
break;
|
mingw: introduce the 'core.hideDotFiles' setting
On Unix (and Linux), files and directories whose names start with a dot
are usually not shown by default. This convention is used by Git: the
.git/ directory should be left alone by regular users, and only accessed
through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden by default, as e.g. Eclipse does not
show them (and the user would therefore be unable to see, say, a
.gitattributes file).
In over five years since the last attempt to bring this patch into core
Git, a slightly buggy version of this patch has served Git for Windows'
users well: no single report indicated problems with the hidden .git/
directory, and the stream of problems caused by the previously non-hidden
.git/ directory simply stopped. The bugs have been fixed during the
process of getting this patch upstream.
Note that there is a funny quirk we have to pay attention to when
creating hidden files: we use Win32's _wopen() function which
transmogrifies its arguments and hands off to Win32's CreateFile()
function. That latter function errors out with ERROR_ACCESS_DENIED (the
equivalent of EACCES) when the equivalent of the O_CREAT flag was passed
and the file attributes (including the hidden flag) do not match an
existing file's. And _wopen() accepts no parameter that would be
transmogrified into said hidden flag. Therefore, we simply try again
without O_CREAT.
A slightly different method is required for our fopen()/freopen()
function as we cannot even *remove* the implicit O_CREAT flag.
Therefore, we briefly mark existing files as unhidden when opening them
via fopen()/freopen().
The ERROR_ACCESS_DENIED error can also be triggered by opening a file
that is marked as a system file (which is unlikely to be tracked in
Git), and by trying to create a file that has *just* been deleted and is
awaiting the last open handles to be released (which would be handled
better by the "Try again?" logic, a story for a different patch series,
though). In both cases, it does not matter much if we try again without
the O_CREAT flag, read: it does not hurt, either.
For details how ERROR_ACCESS_DENIED can be triggered, see
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858
Original-patch-by: Erik Faye-Lund <kusmabite@gmail.com>
Initial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-05-11 08:43:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (hide_dotfiles == HIDE_DOTFILES_TRUE)
|
|
|
|
return *basename == '.';
|
|
|
|
|
|
|
|
assert(hide_dotfiles == HIDE_DOTFILES_DOTGITONLY);
|
|
|
|
return !strncasecmp(".git", basename, 4) &&
|
|
|
|
(!basename[4] || is_dir_sep(basename[4]));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int set_hidden_flag(const wchar_t *path, int set)
|
|
|
|
{
|
|
|
|
DWORD original = GetFileAttributesW(path), modified;
|
|
|
|
if (set)
|
|
|
|
modified = original | FILE_ATTRIBUTE_HIDDEN;
|
|
|
|
else
|
|
|
|
modified = original & ~FILE_ATTRIBUTE_HIDDEN;
|
|
|
|
if (original == modified || SetFileAttributesW(path, modified))
|
|
|
|
return 0;
|
|
|
|
errno = err_win_to_posix(GetLastError());
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-03-15 17:21:28 +00:00
|
|
|
int mingw_mkdir(const char *path, int mode)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
wchar_t wpath[MAX_PATH];
|
mingw: refuse to access paths with trailing spaces or periods
When creating a directory on Windows whose path ends in a space or a
period (or chains thereof), the Win32 API "helpfully" trims those. For
example, `mkdir("abc ");` will return success, but actually create a
directory called `abc` instead.
This stems back to the DOS days, when all file names had exactly 8
characters plus exactly 3 characters for the file extension, and the
only way to have shorter names was by padding with spaces.
Sadly, this "helpful" behavior is a bit inconsistent: after a successful
`mkdir("abc ");`, a `mkdir("abc /def")` will actually _fail_ (because
the directory `abc ` does not actually exist).
Even if it would work, we now have a serious problem because a Git
repository could contain directories `abc` and `abc `, and on Windows,
they would be "merged" unintentionally.
As these paths are illegal on Windows, anyway, let's disallow any
accesses to such paths on that Operating System.
For practical reasons, this behavior is still guarded by the
config setting `core.protectNTFS`: it is possible (and at least two
regression tests make use of it) to create commits without involving the
worktree. In such a scenario, it is of course possible -- even on
Windows -- to create such file names.
Among other consequences, this patch disallows submodules' paths to end
in spaces on Windows (which would formerly have confused Git enough to
try to write into incorrect paths, anyway).
While this patch does not fix a vulnerability on its own, it prevents an
attack vector that was exploited in demonstrations of a number of
recently-fixed security bugs.
The regression test added to `t/t7417-submodule-path-url.sh` reflects
that attack vector.
Note that we have to adjust the test case "prevent git~1 squatting on
Windows" in `t/t7415-submodule-names.sh` because of a very subtle issue.
It tries to clone two submodules whose names differ only in a trailing
period character, and as a consequence their git directories differ in
the same way. Previously, when Git tried to clone the second submodule,
it thought that the git directory already existed (because on Windows,
when you create a directory with the name `b.` it actually creates `b`),
but with this patch, the first submodule's clone will fail because of
the illegal name of the git directory. Therefore, when cloning the
second submodule, Git will take a different code path: a fresh clone
(without an existing git directory). Both code paths fail to clone the
second submodule, both because the the corresponding worktree directory
exists and is not empty, but the error messages are worded differently.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-05 11:27:53 +00:00
|
|
|
|
2019-12-21 22:05:01 +00:00
|
|
|
if (!is_valid_win32_path(path, 0)) {
|
mingw: refuse to access paths with trailing spaces or periods
When creating a directory on Windows whose path ends in a space or a
period (or chains thereof), the Win32 API "helpfully" trims those. For
example, `mkdir("abc ");` will return success, but actually create a
directory called `abc` instead.
This stems back to the DOS days, when all file names had exactly 8
characters plus exactly 3 characters for the file extension, and the
only way to have shorter names was by padding with spaces.
Sadly, this "helpful" behavior is a bit inconsistent: after a successful
`mkdir("abc ");`, a `mkdir("abc /def")` will actually _fail_ (because
the directory `abc ` does not actually exist).
Even if it would work, we now have a serious problem because a Git
repository could contain directories `abc` and `abc `, and on Windows,
they would be "merged" unintentionally.
As these paths are illegal on Windows, anyway, let's disallow any
accesses to such paths on that Operating System.
For practical reasons, this behavior is still guarded by the
config setting `core.protectNTFS`: it is possible (and at least two
regression tests make use of it) to create commits without involving the
worktree. In such a scenario, it is of course possible -- even on
Windows -- to create such file names.
Among other consequences, this patch disallows submodules' paths to end
in spaces on Windows (which would formerly have confused Git enough to
try to write into incorrect paths, anyway).
While this patch does not fix a vulnerability on its own, it prevents an
attack vector that was exploited in demonstrations of a number of
recently-fixed security bugs.
The regression test added to `t/t7417-submodule-path-url.sh` reflects
that attack vector.
Note that we have to adjust the test case "prevent git~1 squatting on
Windows" in `t/t7415-submodule-names.sh` because of a very subtle issue.
It tries to clone two submodules whose names differ only in a trailing
period character, and as a consequence their git directories differ in
the same way. Previously, when Git tried to clone the second submodule,
it thought that the git directory already existed (because on Windows,
when you create a directory with the name `b.` it actually creates `b`),
but with this patch, the first submodule's clone will fail because of
the illegal name of the git directory. Therefore, when cloning the
second submodule, Git will take a different code path: a fresh clone
(without an existing git directory). Both code paths fail to clone the
second submodule, both because the the corresponding worktree directory
exists and is not empty, but the error messages are worded differently.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-05 11:27:53 +00:00
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-03-15 17:21:28 +00:00
|
|
|
if (xutftowcs_path(wpath, path) < 0)
|
|
|
|
return -1;
|
|
|
|
ret = _wmkdir(wpath);
|
mingw: introduce the 'core.hideDotFiles' setting
On Unix (and Linux), files and directories whose names start with a dot
are usually not shown by default. This convention is used by Git: the
.git/ directory should be left alone by regular users, and only accessed
through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden by default, as e.g. Eclipse does not
show them (and the user would therefore be unable to see, say, a
.gitattributes file).
In over five years since the last attempt to bring this patch into core
Git, a slightly buggy version of this patch has served Git for Windows'
users well: no single report indicated problems with the hidden .git/
directory, and the stream of problems caused by the previously non-hidden
.git/ directory simply stopped. The bugs have been fixed during the
process of getting this patch upstream.
Note that there is a funny quirk we have to pay attention to when
creating hidden files: we use Win32's _wopen() function which
transmogrifies its arguments and hands off to Win32's CreateFile()
function. That latter function errors out with ERROR_ACCESS_DENIED (the
equivalent of EACCES) when the equivalent of the O_CREAT flag was passed
and the file attributes (including the hidden flag) do not match an
existing file's. And _wopen() accepts no parameter that would be
transmogrified into said hidden flag. Therefore, we simply try again
without O_CREAT.
A slightly different method is required for our fopen()/freopen()
function as we cannot even *remove* the implicit O_CREAT flag.
Therefore, we briefly mark existing files as unhidden when opening them
via fopen()/freopen().
The ERROR_ACCESS_DENIED error can also be triggered by opening a file
that is marked as a system file (which is unlikely to be tracked in
Git), and by trying to create a file that has *just* been deleted and is
awaiting the last open handles to be released (which would be handled
better by the "Try again?" logic, a story for a different patch series,
though). In both cases, it does not matter much if we try again without
the O_CREAT flag, read: it does not hurt, either.
For details how ERROR_ACCESS_DENIED can be triggered, see
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858
Original-patch-by: Erik Faye-Lund <kusmabite@gmail.com>
Initial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-05-11 08:43:37 +00:00
|
|
|
if (!ret && needs_hiding(path))
|
|
|
|
return set_hidden_flag(wpath, 1);
|
2011-02-07 20:52:34 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-09-11 20:06:02 +00:00
|
|
|
/*
|
|
|
|
* Calling CreateFile() using FILE_APPEND_DATA and without FILE_WRITE_DATA
|
|
|
|
* is documented in [1] as opening a writable file handle in append mode.
|
|
|
|
* (It is believed that) this is atomic since it is maintained by the
|
|
|
|
* kernel unlike the O_APPEND flag which is racily maintained by the CRT.
|
|
|
|
*
|
|
|
|
* [1] https://docs.microsoft.com/en-us/windows/desktop/fileio/file-access-rights-constants
|
|
|
|
*
|
|
|
|
* This trick does not appear to work for named pipes. Instead it creates
|
|
|
|
* a named pipe client handle that cannot be written to. Callers should
|
|
|
|
* just use the regular _wopen() for them. (And since client handle gets
|
|
|
|
* bound to a unique server handle, it isn't really an issue.)
|
|
|
|
*/
|
2018-08-13 19:02:50 +00:00
|
|
|
static int mingw_open_append(wchar_t const *wfilename, int oflags, ...)
|
|
|
|
{
|
|
|
|
HANDLE handle;
|
|
|
|
int fd;
|
|
|
|
DWORD create = (oflags & O_CREAT) ? OPEN_ALWAYS : OPEN_EXISTING;
|
|
|
|
|
|
|
|
/* only these flags are supported */
|
|
|
|
if ((oflags & ~O_CREAT) != (O_WRONLY | O_APPEND))
|
|
|
|
return errno = ENOSYS, -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* FILE_SHARE_WRITE is required to permit child processes
|
|
|
|
* to append to the file.
|
|
|
|
*/
|
|
|
|
handle = CreateFileW(wfilename, FILE_APPEND_DATA,
|
|
|
|
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
|
|
|
NULL, create, FILE_ATTRIBUTE_NORMAL, NULL);
|
2020-04-10 11:28:56 +00:00
|
|
|
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;
|
|
|
|
}
|
2018-09-11 20:06:02 +00:00
|
|
|
|
2018-08-13 19:02:50 +00:00
|
|
|
/*
|
|
|
|
* No O_APPEND here, because the CRT uses it only to reset the
|
2018-09-11 20:06:02 +00:00
|
|
|
* file pointer to EOF before each write(); but that is not
|
|
|
|
* necessary (and may lead to races) for a file created with
|
|
|
|
* FILE_APPEND_DATA.
|
2018-08-13 19:02:50 +00:00
|
|
|
*/
|
|
|
|
fd = _open_osfhandle((intptr_t)handle, O_BINARY);
|
|
|
|
if (fd < 0)
|
|
|
|
CloseHandle(handle);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2018-09-11 20:06:02 +00:00
|
|
|
/*
|
|
|
|
* Does the pathname map to the local named pipe filesystem?
|
|
|
|
* That is, does it have a "//./pipe/" prefix?
|
|
|
|
*/
|
|
|
|
static int is_local_named_pipe_path(const char *filename)
|
|
|
|
{
|
|
|
|
return (is_dir_sep(filename[0]) &&
|
|
|
|
is_dir_sep(filename[1]) &&
|
|
|
|
filename[2] == '.' &&
|
|
|
|
is_dir_sep(filename[3]) &&
|
|
|
|
!strncasecmp(filename+4, "pipe", 4) &&
|
|
|
|
is_dir_sep(filename[8]) &&
|
|
|
|
filename[9]);
|
|
|
|
}
|
|
|
|
|
2007-11-15 21:22:47 +00:00
|
|
|
int mingw_open (const char *filename, int oflags, ...)
|
|
|
|
{
|
2018-08-13 19:02:50 +00:00
|
|
|
typedef int (*open_fn_t)(wchar_t const *wfilename, int oflags, ...);
|
2007-11-15 21:22:47 +00:00
|
|
|
va_list args;
|
|
|
|
unsigned mode;
|
mingw: refuse to access paths with trailing spaces or periods
When creating a directory on Windows whose path ends in a space or a
period (or chains thereof), the Win32 API "helpfully" trims those. For
example, `mkdir("abc ");` will return success, but actually create a
directory called `abc` instead.
This stems back to the DOS days, when all file names had exactly 8
characters plus exactly 3 characters for the file extension, and the
only way to have shorter names was by padding with spaces.
Sadly, this "helpful" behavior is a bit inconsistent: after a successful
`mkdir("abc ");`, a `mkdir("abc /def")` will actually _fail_ (because
the directory `abc ` does not actually exist).
Even if it would work, we now have a serious problem because a Git
repository could contain directories `abc` and `abc `, and on Windows,
they would be "merged" unintentionally.
As these paths are illegal on Windows, anyway, let's disallow any
accesses to such paths on that Operating System.
For practical reasons, this behavior is still guarded by the
config setting `core.protectNTFS`: it is possible (and at least two
regression tests make use of it) to create commits without involving the
worktree. In such a scenario, it is of course possible -- even on
Windows -- to create such file names.
Among other consequences, this patch disallows submodules' paths to end
in spaces on Windows (which would formerly have confused Git enough to
try to write into incorrect paths, anyway).
While this patch does not fix a vulnerability on its own, it prevents an
attack vector that was exploited in demonstrations of a number of
recently-fixed security bugs.
The regression test added to `t/t7417-submodule-path-url.sh` reflects
that attack vector.
Note that we have to adjust the test case "prevent git~1 squatting on
Windows" in `t/t7415-submodule-names.sh` because of a very subtle issue.
It tries to clone two submodules whose names differ only in a trailing
period character, and as a consequence their git directories differ in
the same way. Previously, when Git tried to clone the second submodule,
it thought that the git directory already existed (because on Windows,
when you create a directory with the name `b.` it actually creates `b`),
but with this patch, the first submodule's clone will fail because of
the illegal name of the git directory. Therefore, when cloning the
second submodule, Git will take a different code path: a fresh clone
(without an existing git directory). Both code paths fail to clone the
second submodule, both because the the corresponding worktree directory
exists and is not empty, but the error messages are worded differently.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-05 11:27:53 +00:00
|
|
|
int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
|
2012-03-15 17:21:28 +00:00
|
|
|
wchar_t wfilename[MAX_PATH];
|
2018-08-13 19:02:50 +00:00
|
|
|
open_fn_t open_fn;
|
2009-09-16 08:20:17 +00:00
|
|
|
|
2007-11-15 21:22:47 +00:00
|
|
|
va_start(args, oflags);
|
|
|
|
mode = va_arg(args, int);
|
|
|
|
va_end(args);
|
|
|
|
|
2019-12-21 22:05:01 +00:00
|
|
|
if (!is_valid_win32_path(filename, !create)) {
|
mingw: refuse to access paths with trailing spaces or periods
When creating a directory on Windows whose path ends in a space or a
period (or chains thereof), the Win32 API "helpfully" trims those. For
example, `mkdir("abc ");` will return success, but actually create a
directory called `abc` instead.
This stems back to the DOS days, when all file names had exactly 8
characters plus exactly 3 characters for the file extension, and the
only way to have shorter names was by padding with spaces.
Sadly, this "helpful" behavior is a bit inconsistent: after a successful
`mkdir("abc ");`, a `mkdir("abc /def")` will actually _fail_ (because
the directory `abc ` does not actually exist).
Even if it would work, we now have a serious problem because a Git
repository could contain directories `abc` and `abc `, and on Windows,
they would be "merged" unintentionally.
As these paths are illegal on Windows, anyway, let's disallow any
accesses to such paths on that Operating System.
For practical reasons, this behavior is still guarded by the
config setting `core.protectNTFS`: it is possible (and at least two
regression tests make use of it) to create commits without involving the
worktree. In such a scenario, it is of course possible -- even on
Windows -- to create such file names.
Among other consequences, this patch disallows submodules' paths to end
in spaces on Windows (which would formerly have confused Git enough to
try to write into incorrect paths, anyway).
While this patch does not fix a vulnerability on its own, it prevents an
attack vector that was exploited in demonstrations of a number of
recently-fixed security bugs.
The regression test added to `t/t7417-submodule-path-url.sh` reflects
that attack vector.
Note that we have to adjust the test case "prevent git~1 squatting on
Windows" in `t/t7415-submodule-names.sh` because of a very subtle issue.
It tries to clone two submodules whose names differ only in a trailing
period character, and as a consequence their git directories differ in
the same way. Previously, when Git tried to clone the second submodule,
it thought that the git directory already existed (because on Windows,
when you create a directory with the name `b.` it actually creates `b`),
but with this patch, the first submodule's clone will fail because of
the illegal name of the git directory. Therefore, when cloning the
second submodule, Git will take a different code path: a fresh clone
(without an existing git directory). Both code paths fail to clone the
second submodule, both because the the corresponding worktree directory
exists and is not empty, but the error messages are worded differently.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-05 11:27:53 +00:00
|
|
|
errno = create ? EINVAL : ENOENT;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-09-11 20:06:02 +00:00
|
|
|
if ((oflags & O_APPEND) && !is_local_named_pipe_path(filename))
|
2018-08-13 19:02:50 +00:00
|
|
|
open_fn = mingw_open_append;
|
|
|
|
else
|
|
|
|
open_fn = _wopen;
|
|
|
|
|
2019-12-21 22:05:00 +00:00
|
|
|
if (filename && !strcmp(filename, "/dev/null"))
|
|
|
|
wcscpy(wfilename, L"nul");
|
|
|
|
else if (xutftowcs_path(wfilename, filename) < 0)
|
2012-03-15 17:21:28 +00:00
|
|
|
return -1;
|
2019-12-21 22:05:00 +00:00
|
|
|
|
2018-08-13 19:02:50 +00:00
|
|
|
fd = open_fn(wfilename, oflags, mode);
|
2009-09-16 08:20:17 +00:00
|
|
|
|
2014-11-16 21:06:26 +00:00
|
|
|
if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
|
2012-03-15 17:21:28 +00:00
|
|
|
DWORD attrs = GetFileAttributesW(wfilename);
|
2007-11-15 21:22:47 +00:00
|
|
|
if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
|
|
|
|
errno = EISDIR;
|
|
|
|
}
|
mingw: introduce the 'core.hideDotFiles' setting
On Unix (and Linux), files and directories whose names start with a dot
are usually not shown by default. This convention is used by Git: the
.git/ directory should be left alone by regular users, and only accessed
through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden by default, as e.g. Eclipse does not
show them (and the user would therefore be unable to see, say, a
.gitattributes file).
In over five years since the last attempt to bring this patch into core
Git, a slightly buggy version of this patch has served Git for Windows'
users well: no single report indicated problems with the hidden .git/
directory, and the stream of problems caused by the previously non-hidden
.git/ directory simply stopped. The bugs have been fixed during the
process of getting this patch upstream.
Note that there is a funny quirk we have to pay attention to when
creating hidden files: we use Win32's _wopen() function which
transmogrifies its arguments and hands off to Win32's CreateFile()
function. That latter function errors out with ERROR_ACCESS_DENIED (the
equivalent of EACCES) when the equivalent of the O_CREAT flag was passed
and the file attributes (including the hidden flag) do not match an
existing file's. And _wopen() accepts no parameter that would be
transmogrified into said hidden flag. Therefore, we simply try again
without O_CREAT.
A slightly different method is required for our fopen()/freopen()
function as we cannot even *remove* the implicit O_CREAT flag.
Therefore, we briefly mark existing files as unhidden when opening them
via fopen()/freopen().
The ERROR_ACCESS_DENIED error can also be triggered by opening a file
that is marked as a system file (which is unlikely to be tracked in
Git), and by trying to create a file that has *just* been deleted and is
awaiting the last open handles to be released (which would be handled
better by the "Try again?" logic, a story for a different patch series,
though). In both cases, it does not matter much if we try again without
the O_CREAT flag, read: it does not hurt, either.
For details how ERROR_ACCESS_DENIED can be triggered, see
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858
Original-patch-by: Erik Faye-Lund <kusmabite@gmail.com>
Initial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-05-11 08:43:37 +00:00
|
|
|
if ((oflags & O_CREAT) && needs_hiding(filename)) {
|
|
|
|
/*
|
|
|
|
* Internally, _wopen() uses the CreateFile() API which errors
|
|
|
|
* out with an ERROR_ACCESS_DENIED if CREATE_ALWAYS was
|
|
|
|
* specified and an already existing file's attributes do not
|
|
|
|
* match *exactly*. As there is no mode or flag we can set that
|
|
|
|
* would correspond to FILE_ATTRIBUTE_HIDDEN, let's just try
|
|
|
|
* again *without* the O_CREAT flag (that corresponds to the
|
|
|
|
* CREATE_ALWAYS flag of CreateFile()).
|
|
|
|
*/
|
|
|
|
if (fd < 0 && errno == EACCES)
|
2018-08-13 19:02:50 +00:00
|
|
|
fd = open_fn(wfilename, oflags & ~O_CREAT, mode);
|
mingw: introduce the 'core.hideDotFiles' setting
On Unix (and Linux), files and directories whose names start with a dot
are usually not shown by default. This convention is used by Git: the
.git/ directory should be left alone by regular users, and only accessed
through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden by default, as e.g. Eclipse does not
show them (and the user would therefore be unable to see, say, a
.gitattributes file).
In over five years since the last attempt to bring this patch into core
Git, a slightly buggy version of this patch has served Git for Windows'
users well: no single report indicated problems with the hidden .git/
directory, and the stream of problems caused by the previously non-hidden
.git/ directory simply stopped. The bugs have been fixed during the
process of getting this patch upstream.
Note that there is a funny quirk we have to pay attention to when
creating hidden files: we use Win32's _wopen() function which
transmogrifies its arguments and hands off to Win32's CreateFile()
function. That latter function errors out with ERROR_ACCESS_DENIED (the
equivalent of EACCES) when the equivalent of the O_CREAT flag was passed
and the file attributes (including the hidden flag) do not match an
existing file's. And _wopen() accepts no parameter that would be
transmogrified into said hidden flag. Therefore, we simply try again
without O_CREAT.
A slightly different method is required for our fopen()/freopen()
function as we cannot even *remove* the implicit O_CREAT flag.
Therefore, we briefly mark existing files as unhidden when opening them
via fopen()/freopen().
The ERROR_ACCESS_DENIED error can also be triggered by opening a file
that is marked as a system file (which is unlikely to be tracked in
Git), and by trying to create a file that has *just* been deleted and is
awaiting the last open handles to be released (which would be handled
better by the "Try again?" logic, a story for a different patch series,
though). In both cases, it does not matter much if we try again without
the O_CREAT flag, read: it does not hurt, either.
For details how ERROR_ACCESS_DENIED can be triggered, see
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858
Original-patch-by: Erik Faye-Lund <kusmabite@gmail.com>
Initial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-05-11 08:43:37 +00:00
|
|
|
if (fd >= 0 && set_hidden_flag(wfilename, 1))
|
|
|
|
warning("could not mark '%s' as hidden.", filename);
|
|
|
|
}
|
2007-11-15 21:22:47 +00:00
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
2012-12-04 08:10:38 +00:00
|
|
|
static BOOL WINAPI ctrl_ignore(DWORD type)
|
|
|
|
{
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef fgetc
|
|
|
|
int mingw_fgetc(FILE *stream)
|
|
|
|
{
|
|
|
|
int ch;
|
|
|
|
if (!isatty(_fileno(stream)))
|
|
|
|
return fgetc(stream);
|
|
|
|
|
|
|
|
SetConsoleCtrlHandler(ctrl_ignore, TRUE);
|
|
|
|
while (1) {
|
|
|
|
ch = fgetc(stream);
|
|
|
|
if (ch != EOF || GetLastError() != ERROR_OPERATION_ABORTED)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Ctrl+C was pressed, simulate SIGINT and retry */
|
|
|
|
mingw_raise(SIGINT);
|
|
|
|
}
|
|
|
|
SetConsoleCtrlHandler(ctrl_ignore, FALSE);
|
|
|
|
return ch;
|
|
|
|
}
|
|
|
|
|
2010-02-25 20:03:44 +00:00
|
|
|
#undef fopen
|
|
|
|
FILE *mingw_fopen (const char *filename, const char *otype)
|
|
|
|
{
|
mingw: introduce the 'core.hideDotFiles' setting
On Unix (and Linux), files and directories whose names start with a dot
are usually not shown by default. This convention is used by Git: the
.git/ directory should be left alone by regular users, and only accessed
through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden by default, as e.g. Eclipse does not
show them (and the user would therefore be unable to see, say, a
.gitattributes file).
In over five years since the last attempt to bring this patch into core
Git, a slightly buggy version of this patch has served Git for Windows'
users well: no single report indicated problems with the hidden .git/
directory, and the stream of problems caused by the previously non-hidden
.git/ directory simply stopped. The bugs have been fixed during the
process of getting this patch upstream.
Note that there is a funny quirk we have to pay attention to when
creating hidden files: we use Win32's _wopen() function which
transmogrifies its arguments and hands off to Win32's CreateFile()
function. That latter function errors out with ERROR_ACCESS_DENIED (the
equivalent of EACCES) when the equivalent of the O_CREAT flag was passed
and the file attributes (including the hidden flag) do not match an
existing file's. And _wopen() accepts no parameter that would be
transmogrified into said hidden flag. Therefore, we simply try again
without O_CREAT.
A slightly different method is required for our fopen()/freopen()
function as we cannot even *remove* the implicit O_CREAT flag.
Therefore, we briefly mark existing files as unhidden when opening them
via fopen()/freopen().
The ERROR_ACCESS_DENIED error can also be triggered by opening a file
that is marked as a system file (which is unlikely to be tracked in
Git), and by trying to create a file that has *just* been deleted and is
awaiting the last open handles to be released (which would be handled
better by the "Try again?" logic, a story for a different patch series,
though). In both cases, it does not matter much if we try again without
the O_CREAT flag, read: it does not hurt, either.
For details how ERROR_ACCESS_DENIED can be triggered, see
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858
Original-patch-by: Erik Faye-Lund <kusmabite@gmail.com>
Initial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-05-11 08:43:37 +00:00
|
|
|
int hide = needs_hiding(filename);
|
2012-03-15 17:21:28 +00:00
|
|
|
FILE *file;
|
|
|
|
wchar_t wfilename[MAX_PATH], wotype[4];
|
2019-12-21 22:05:01 +00:00
|
|
|
if (filename && !strcmp(filename, "/dev/null"))
|
|
|
|
wcscpy(wfilename, L"nul");
|
|
|
|
else if (!is_valid_win32_path(filename, 1)) {
|
mingw: refuse to access paths with trailing spaces or periods
When creating a directory on Windows whose path ends in a space or a
period (or chains thereof), the Win32 API "helpfully" trims those. For
example, `mkdir("abc ");` will return success, but actually create a
directory called `abc` instead.
This stems back to the DOS days, when all file names had exactly 8
characters plus exactly 3 characters for the file extension, and the
only way to have shorter names was by padding with spaces.
Sadly, this "helpful" behavior is a bit inconsistent: after a successful
`mkdir("abc ");`, a `mkdir("abc /def")` will actually _fail_ (because
the directory `abc ` does not actually exist).
Even if it would work, we now have a serious problem because a Git
repository could contain directories `abc` and `abc `, and on Windows,
they would be "merged" unintentionally.
As these paths are illegal on Windows, anyway, let's disallow any
accesses to such paths on that Operating System.
For practical reasons, this behavior is still guarded by the
config setting `core.protectNTFS`: it is possible (and at least two
regression tests make use of it) to create commits without involving the
worktree. In such a scenario, it is of course possible -- even on
Windows -- to create such file names.
Among other consequences, this patch disallows submodules' paths to end
in spaces on Windows (which would formerly have confused Git enough to
try to write into incorrect paths, anyway).
While this patch does not fix a vulnerability on its own, it prevents an
attack vector that was exploited in demonstrations of a number of
recently-fixed security bugs.
The regression test added to `t/t7417-submodule-path-url.sh` reflects
that attack vector.
Note that we have to adjust the test case "prevent git~1 squatting on
Windows" in `t/t7415-submodule-names.sh` because of a very subtle issue.
It tries to clone two submodules whose names differ only in a trailing
period character, and as a consequence their git directories differ in
the same way. Previously, when Git tried to clone the second submodule,
it thought that the git directory already existed (because on Windows,
when you create a directory with the name `b.` it actually creates `b`),
but with this patch, the first submodule's clone will fail because of
the illegal name of the git directory. Therefore, when cloning the
second submodule, Git will take a different code path: a fresh clone
(without an existing git directory). Both code paths fail to clone the
second submodule, both because the the corresponding worktree directory
exists and is not empty, but the error messages are worded differently.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-05 11:27:53 +00:00
|
|
|
int create = otype && strchr(otype, 'w');
|
|
|
|
errno = create ? EINVAL : ENOENT;
|
|
|
|
return NULL;
|
2019-12-21 22:05:01 +00:00
|
|
|
} else if (xutftowcs_path(wfilename, filename) < 0)
|
2012-03-15 17:21:28 +00:00
|
|
|
return NULL;
|
2019-12-21 22:05:00 +00:00
|
|
|
|
|
|
|
if (xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
|
2012-03-15 17:21:28 +00:00
|
|
|
return NULL;
|
2019-12-21 22:05:00 +00:00
|
|
|
|
mingw: introduce the 'core.hideDotFiles' setting
On Unix (and Linux), files and directories whose names start with a dot
are usually not shown by default. This convention is used by Git: the
.git/ directory should be left alone by regular users, and only accessed
through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden by default, as e.g. Eclipse does not
show them (and the user would therefore be unable to see, say, a
.gitattributes file).
In over five years since the last attempt to bring this patch into core
Git, a slightly buggy version of this patch has served Git for Windows'
users well: no single report indicated problems with the hidden .git/
directory, and the stream of problems caused by the previously non-hidden
.git/ directory simply stopped. The bugs have been fixed during the
process of getting this patch upstream.
Note that there is a funny quirk we have to pay attention to when
creating hidden files: we use Win32's _wopen() function which
transmogrifies its arguments and hands off to Win32's CreateFile()
function. That latter function errors out with ERROR_ACCESS_DENIED (the
equivalent of EACCES) when the equivalent of the O_CREAT flag was passed
and the file attributes (including the hidden flag) do not match an
existing file's. And _wopen() accepts no parameter that would be
transmogrified into said hidden flag. Therefore, we simply try again
without O_CREAT.
A slightly different method is required for our fopen()/freopen()
function as we cannot even *remove* the implicit O_CREAT flag.
Therefore, we briefly mark existing files as unhidden when opening them
via fopen()/freopen().
The ERROR_ACCESS_DENIED error can also be triggered by opening a file
that is marked as a system file (which is unlikely to be tracked in
Git), and by trying to create a file that has *just* been deleted and is
awaiting the last open handles to be released (which would be handled
better by the "Try again?" logic, a story for a different patch series,
though). In both cases, it does not matter much if we try again without
the O_CREAT flag, read: it does not hurt, either.
For details how ERROR_ACCESS_DENIED can be triggered, see
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858
Original-patch-by: Erik Faye-Lund <kusmabite@gmail.com>
Initial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-05-11 08:43:37 +00:00
|
|
|
if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
|
|
|
|
error("could not unhide %s", filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-03-15 17:21:28 +00:00
|
|
|
file = _wfopen(wfilename, wotype);
|
2017-05-29 20:27:35 +00:00
|
|
|
if (!file && GetLastError() == ERROR_INVALID_NAME)
|
|
|
|
errno = ENOENT;
|
mingw: introduce the 'core.hideDotFiles' setting
On Unix (and Linux), files and directories whose names start with a dot
are usually not shown by default. This convention is used by Git: the
.git/ directory should be left alone by regular users, and only accessed
through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden by default, as e.g. Eclipse does not
show them (and the user would therefore be unable to see, say, a
.gitattributes file).
In over five years since the last attempt to bring this patch into core
Git, a slightly buggy version of this patch has served Git for Windows'
users well: no single report indicated problems with the hidden .git/
directory, and the stream of problems caused by the previously non-hidden
.git/ directory simply stopped. The bugs have been fixed during the
process of getting this patch upstream.
Note that there is a funny quirk we have to pay attention to when
creating hidden files: we use Win32's _wopen() function which
transmogrifies its arguments and hands off to Win32's CreateFile()
function. That latter function errors out with ERROR_ACCESS_DENIED (the
equivalent of EACCES) when the equivalent of the O_CREAT flag was passed
and the file attributes (including the hidden flag) do not match an
existing file's. And _wopen() accepts no parameter that would be
transmogrified into said hidden flag. Therefore, we simply try again
without O_CREAT.
A slightly different method is required for our fopen()/freopen()
function as we cannot even *remove* the implicit O_CREAT flag.
Therefore, we briefly mark existing files as unhidden when opening them
via fopen()/freopen().
The ERROR_ACCESS_DENIED error can also be triggered by opening a file
that is marked as a system file (which is unlikely to be tracked in
Git), and by trying to create a file that has *just* been deleted and is
awaiting the last open handles to be released (which would be handled
better by the "Try again?" logic, a story for a different patch series,
though). In both cases, it does not matter much if we try again without
the O_CREAT flag, read: it does not hurt, either.
For details how ERROR_ACCESS_DENIED can be triggered, see
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858
Original-patch-by: Erik Faye-Lund <kusmabite@gmail.com>
Initial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-05-11 08:43:37 +00:00
|
|
|
if (file && hide && set_hidden_flag(wfilename, 1))
|
|
|
|
warning("could not mark '%s' as hidden.", filename);
|
2012-03-15 17:21:28 +00:00
|
|
|
return file;
|
2010-02-25 20:03:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
|
|
|
|
{
|
mingw: introduce the 'core.hideDotFiles' setting
On Unix (and Linux), files and directories whose names start with a dot
are usually not shown by default. This convention is used by Git: the
.git/ directory should be left alone by regular users, and only accessed
through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden by default, as e.g. Eclipse does not
show them (and the user would therefore be unable to see, say, a
.gitattributes file).
In over five years since the last attempt to bring this patch into core
Git, a slightly buggy version of this patch has served Git for Windows'
users well: no single report indicated problems with the hidden .git/
directory, and the stream of problems caused by the previously non-hidden
.git/ directory simply stopped. The bugs have been fixed during the
process of getting this patch upstream.
Note that there is a funny quirk we have to pay attention to when
creating hidden files: we use Win32's _wopen() function which
transmogrifies its arguments and hands off to Win32's CreateFile()
function. That latter function errors out with ERROR_ACCESS_DENIED (the
equivalent of EACCES) when the equivalent of the O_CREAT flag was passed
and the file attributes (including the hidden flag) do not match an
existing file's. And _wopen() accepts no parameter that would be
transmogrified into said hidden flag. Therefore, we simply try again
without O_CREAT.
A slightly different method is required for our fopen()/freopen()
function as we cannot even *remove* the implicit O_CREAT flag.
Therefore, we briefly mark existing files as unhidden when opening them
via fopen()/freopen().
The ERROR_ACCESS_DENIED error can also be triggered by opening a file
that is marked as a system file (which is unlikely to be tracked in
Git), and by trying to create a file that has *just* been deleted and is
awaiting the last open handles to be released (which would be handled
better by the "Try again?" logic, a story for a different patch series,
though). In both cases, it does not matter much if we try again without
the O_CREAT flag, read: it does not hurt, either.
For details how ERROR_ACCESS_DENIED can be triggered, see
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858
Original-patch-by: Erik Faye-Lund <kusmabite@gmail.com>
Initial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-05-11 08:43:37 +00:00
|
|
|
int hide = needs_hiding(filename);
|
2012-03-15 17:21:28 +00:00
|
|
|
FILE *file;
|
|
|
|
wchar_t wfilename[MAX_PATH], wotype[4];
|
2019-12-21 22:05:01 +00:00
|
|
|
if (filename && !strcmp(filename, "/dev/null"))
|
|
|
|
wcscpy(wfilename, L"nul");
|
|
|
|
else if (!is_valid_win32_path(filename, 1)) {
|
mingw: refuse to access paths with trailing spaces or periods
When creating a directory on Windows whose path ends in a space or a
period (or chains thereof), the Win32 API "helpfully" trims those. For
example, `mkdir("abc ");` will return success, but actually create a
directory called `abc` instead.
This stems back to the DOS days, when all file names had exactly 8
characters plus exactly 3 characters for the file extension, and the
only way to have shorter names was by padding with spaces.
Sadly, this "helpful" behavior is a bit inconsistent: after a successful
`mkdir("abc ");`, a `mkdir("abc /def")` will actually _fail_ (because
the directory `abc ` does not actually exist).
Even if it would work, we now have a serious problem because a Git
repository could contain directories `abc` and `abc `, and on Windows,
they would be "merged" unintentionally.
As these paths are illegal on Windows, anyway, let's disallow any
accesses to such paths on that Operating System.
For practical reasons, this behavior is still guarded by the
config setting `core.protectNTFS`: it is possible (and at least two
regression tests make use of it) to create commits without involving the
worktree. In such a scenario, it is of course possible -- even on
Windows -- to create such file names.
Among other consequences, this patch disallows submodules' paths to end
in spaces on Windows (which would formerly have confused Git enough to
try to write into incorrect paths, anyway).
While this patch does not fix a vulnerability on its own, it prevents an
attack vector that was exploited in demonstrations of a number of
recently-fixed security bugs.
The regression test added to `t/t7417-submodule-path-url.sh` reflects
that attack vector.
Note that we have to adjust the test case "prevent git~1 squatting on
Windows" in `t/t7415-submodule-names.sh` because of a very subtle issue.
It tries to clone two submodules whose names differ only in a trailing
period character, and as a consequence their git directories differ in
the same way. Previously, when Git tried to clone the second submodule,
it thought that the git directory already existed (because on Windows,
when you create a directory with the name `b.` it actually creates `b`),
but with this patch, the first submodule's clone will fail because of
the illegal name of the git directory. Therefore, when cloning the
second submodule, Git will take a different code path: a fresh clone
(without an existing git directory). Both code paths fail to clone the
second submodule, both because the the corresponding worktree directory
exists and is not empty, but the error messages are worded differently.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-05 11:27:53 +00:00
|
|
|
int create = otype && strchr(otype, 'w');
|
|
|
|
errno = create ? EINVAL : ENOENT;
|
|
|
|
return NULL;
|
2019-12-21 22:05:01 +00:00
|
|
|
} else if (xutftowcs_path(wfilename, filename) < 0)
|
2012-03-15 17:21:28 +00:00
|
|
|
return NULL;
|
2019-12-21 22:05:00 +00:00
|
|
|
|
|
|
|
if (xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
|
2012-03-15 17:21:28 +00:00
|
|
|
return NULL;
|
2019-12-21 22:05:00 +00:00
|
|
|
|
mingw: introduce the 'core.hideDotFiles' setting
On Unix (and Linux), files and directories whose names start with a dot
are usually not shown by default. This convention is used by Git: the
.git/ directory should be left alone by regular users, and only accessed
through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden by default, as e.g. Eclipse does not
show them (and the user would therefore be unable to see, say, a
.gitattributes file).
In over five years since the last attempt to bring this patch into core
Git, a slightly buggy version of this patch has served Git for Windows'
users well: no single report indicated problems with the hidden .git/
directory, and the stream of problems caused by the previously non-hidden
.git/ directory simply stopped. The bugs have been fixed during the
process of getting this patch upstream.
Note that there is a funny quirk we have to pay attention to when
creating hidden files: we use Win32's _wopen() function which
transmogrifies its arguments and hands off to Win32's CreateFile()
function. That latter function errors out with ERROR_ACCESS_DENIED (the
equivalent of EACCES) when the equivalent of the O_CREAT flag was passed
and the file attributes (including the hidden flag) do not match an
existing file's. And _wopen() accepts no parameter that would be
transmogrified into said hidden flag. Therefore, we simply try again
without O_CREAT.
A slightly different method is required for our fopen()/freopen()
function as we cannot even *remove* the implicit O_CREAT flag.
Therefore, we briefly mark existing files as unhidden when opening them
via fopen()/freopen().
The ERROR_ACCESS_DENIED error can also be triggered by opening a file
that is marked as a system file (which is unlikely to be tracked in
Git), and by trying to create a file that has *just* been deleted and is
awaiting the last open handles to be released (which would be handled
better by the "Try again?" logic, a story for a different patch series,
though). In both cases, it does not matter much if we try again without
the O_CREAT flag, read: it does not hurt, either.
For details how ERROR_ACCESS_DENIED can be triggered, see
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858
Original-patch-by: Erik Faye-Lund <kusmabite@gmail.com>
Initial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-05-11 08:43:37 +00:00
|
|
|
if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
|
|
|
|
error("could not unhide %s", filename);
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-03-15 17:21:28 +00:00
|
|
|
file = _wfreopen(wfilename, wotype, stream);
|
mingw: introduce the 'core.hideDotFiles' setting
On Unix (and Linux), files and directories whose names start with a dot
are usually not shown by default. This convention is used by Git: the
.git/ directory should be left alone by regular users, and only accessed
through Git itself.
On Windows, no such convention exists. Instead, there is an explicit flag
to mark files or directories as hidden.
In the early days, Git for Windows did not mark the .git/ directory (or
for that matter, any file or directory whose name starts with a dot)
hidden. This lead to quite a bit of confusion, and even loss of data.
Consequently, Git for Windows introduced the core.hideDotFiles setting,
with three possible values: true, false, and dotGitOnly, defaulting to
marking only the .git/ directory as hidden.
The rationale: users do not need to access .git/ directly, and indeed (as
was demonstrated) should not really see that directory, either. However,
not all dot files should be hidden by default, as e.g. Eclipse does not
show them (and the user would therefore be unable to see, say, a
.gitattributes file).
In over five years since the last attempt to bring this patch into core
Git, a slightly buggy version of this patch has served Git for Windows'
users well: no single report indicated problems with the hidden .git/
directory, and the stream of problems caused by the previously non-hidden
.git/ directory simply stopped. The bugs have been fixed during the
process of getting this patch upstream.
Note that there is a funny quirk we have to pay attention to when
creating hidden files: we use Win32's _wopen() function which
transmogrifies its arguments and hands off to Win32's CreateFile()
function. That latter function errors out with ERROR_ACCESS_DENIED (the
equivalent of EACCES) when the equivalent of the O_CREAT flag was passed
and the file attributes (including the hidden flag) do not match an
existing file's. And _wopen() accepts no parameter that would be
transmogrified into said hidden flag. Therefore, we simply try again
without O_CREAT.
A slightly different method is required for our fopen()/freopen()
function as we cannot even *remove* the implicit O_CREAT flag.
Therefore, we briefly mark existing files as unhidden when opening them
via fopen()/freopen().
The ERROR_ACCESS_DENIED error can also be triggered by opening a file
that is marked as a system file (which is unlikely to be tracked in
Git), and by trying to create a file that has *just* been deleted and is
awaiting the last open handles to be released (which would be handled
better by the "Try again?" logic, a story for a different patch series,
though). In both cases, it does not matter much if we try again without
the O_CREAT flag, read: it does not hurt, either.
For details how ERROR_ACCESS_DENIED can be triggered, see
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858
Original-patch-by: Erik Faye-Lund <kusmabite@gmail.com>
Initial-Test-By: Pat Thoyts <patthoyts@users.sourceforge.net>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-05-11 08:43:37 +00:00
|
|
|
if (file && hide && set_hidden_flag(wfilename, 1))
|
|
|
|
warning("could not mark '%s' as hidden.", filename);
|
2012-03-15 17:21:28 +00:00
|
|
|
return file;
|
2010-02-25 20:03:44 +00:00
|
|
|
}
|
|
|
|
|
2012-10-17 07:05:51 +00:00
|
|
|
#undef fflush
|
|
|
|
int mingw_fflush(FILE *stream)
|
|
|
|
{
|
|
|
|
int ret = fflush(stream);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* write() is used behind the scenes of stdio output functions.
|
|
|
|
* Since git code does not check for errors after each stdio write
|
|
|
|
* operation, it can happen that write() is called by a later
|
|
|
|
* stdio function even if an earlier write() call failed. In the
|
|
|
|
* case of a pipe whose readable end was closed, only the first
|
|
|
|
* call to write() reports EPIPE on Windows. Subsequent write()
|
|
|
|
* calls report EINVAL. It is impossible to notice whether this
|
|
|
|
* fflush invocation triggered such a case, therefore, we have to
|
|
|
|
* catch all EINVAL errors whole-sale.
|
|
|
|
*/
|
|
|
|
if (ret && errno == EINVAL)
|
|
|
|
errno = EPIPE;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2015-12-17 17:08:15 +00:00
|
|
|
#undef write
|
|
|
|
ssize_t mingw_write(int fd, const void *buf, size_t len)
|
|
|
|
{
|
|
|
|
ssize_t result = write(fd, buf, len);
|
|
|
|
|
|
|
|
if (result < 0 && errno == EINVAL && buf) {
|
|
|
|
/* check if fd is a pipe */
|
|
|
|
HANDLE h = (HANDLE) _get_osfhandle(fd);
|
|
|
|
if (GetFileType(h) == FILE_TYPE_PIPE)
|
|
|
|
errno = EPIPE;
|
|
|
|
else
|
|
|
|
errno = EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2012-03-15 17:21:28 +00:00
|
|
|
int mingw_access(const char *filename, int mode)
|
|
|
|
{
|
|
|
|
wchar_t wfilename[MAX_PATH];
|
2021-04-16 11:21:01 +00:00
|
|
|
if (!strcmp("nul", filename) || !strcmp("/dev/null", filename))
|
|
|
|
return 0;
|
2012-03-15 17:21:28 +00:00
|
|
|
if (xutftowcs_path(wfilename, filename) < 0)
|
|
|
|
return -1;
|
|
|
|
/* X_OK is not supported by the MSVCRT version */
|
|
|
|
return _waccess(wfilename, mode & ~X_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
int mingw_chdir(const char *dirname)
|
|
|
|
{
|
|
|
|
wchar_t wdirname[MAX_PATH];
|
|
|
|
if (xutftowcs_path(wdirname, dirname) < 0)
|
|
|
|
return -1;
|
|
|
|
return _wchdir(wdirname);
|
|
|
|
}
|
|
|
|
|
|
|
|
int mingw_chmod(const char *filename, int mode)
|
|
|
|
{
|
|
|
|
wchar_t wfilename[MAX_PATH];
|
|
|
|
if (xutftowcs_path(wfilename, filename) < 0)
|
|
|
|
return -1;
|
|
|
|
return _wchmod(wfilename, mode);
|
|
|
|
}
|
|
|
|
|
2010-01-15 20:12:21 +00:00
|
|
|
/*
|
|
|
|
* The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
|
|
|
|
* Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
|
|
|
|
*/
|
|
|
|
static inline long long filetime_to_hnsec(const FILETIME *ft)
|
2007-09-03 18:40:26 +00:00
|
|
|
{
|
|
|
|
long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
|
2010-01-15 20:12:21 +00:00
|
|
|
/* Windows to Unix Epoch conversion */
|
|
|
|
return winTime - 116444736000000000LL;
|
|
|
|
}
|
|
|
|
|
2018-10-23 10:23:22 +00:00
|
|
|
static inline void filetime_to_timespec(const FILETIME *ft, struct timespec *ts)
|
2010-01-15 20:12:21 +00:00
|
|
|
{
|
2018-10-23 10:23:22 +00:00
|
|
|
long long hnsec = filetime_to_hnsec(ft);
|
|
|
|
ts->tv_sec = (time_t)(hnsec / 10000000);
|
|
|
|
ts->tv_nsec = (hnsec % 10000000) * 100;
|
2007-09-03 18:40:26 +00:00
|
|
|
}
|
|
|
|
|
2016-01-26 14:34:52 +00:00
|
|
|
/**
|
|
|
|
* Verifies that safe_create_leading_directories() would succeed.
|
|
|
|
*/
|
|
|
|
static int has_valid_directory_prefix(wchar_t *wfilename)
|
|
|
|
{
|
|
|
|
int n = wcslen(wfilename);
|
|
|
|
|
|
|
|
while (n > 0) {
|
|
|
|
wchar_t c = wfilename[--n];
|
|
|
|
DWORD attributes;
|
|
|
|
|
|
|
|
if (!is_dir_sep(c))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
wfilename[n] = L'\0';
|
|
|
|
attributes = GetFileAttributesW(wfilename);
|
|
|
|
wfilename[n] = c;
|
|
|
|
if (attributes == FILE_ATTRIBUTE_DIRECTORY ||
|
|
|
|
attributes == FILE_ATTRIBUTE_DEVICE)
|
|
|
|
return 1;
|
|
|
|
if (attributes == INVALID_FILE_ATTRIBUTES)
|
|
|
|
switch (GetLastError()) {
|
|
|
|
case ERROR_PATH_NOT_FOUND:
|
|
|
|
continue;
|
|
|
|
case ERROR_FILE_NOT_FOUND:
|
|
|
|
/* This implies parent directory exists. */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2007-09-03 18:40:26 +00:00
|
|
|
/* We keep the do_lstat code in a separate function to avoid recursion.
|
|
|
|
* When a path ends with a slash, the stat will fail with ENOENT. In
|
|
|
|
* this case, we strip the trailing slashes and stat again.
|
2010-03-17 15:17:34 +00:00
|
|
|
*
|
|
|
|
* If follow is true then act like stat() and report on the link
|
|
|
|
* target. Otherwise report on the link itself.
|
2007-09-03 18:40:26 +00:00
|
|
|
*/
|
2010-03-17 15:17:34 +00:00
|
|
|
static int do_lstat(int follow, const char *file_name, struct stat *buf)
|
2007-09-03 18:40:26 +00:00
|
|
|
{
|
|
|
|
WIN32_FILE_ATTRIBUTE_DATA fdata;
|
2012-03-15 17:21:28 +00:00
|
|
|
wchar_t wfilename[MAX_PATH];
|
|
|
|
if (xutftowcs_path(wfilename, file_name) < 0)
|
|
|
|
return -1;
|
2007-09-03 18:40:26 +00:00
|
|
|
|
2012-03-15 17:21:28 +00:00
|
|
|
if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
|
2007-09-03 18:40:26 +00:00
|
|
|
buf->st_ino = 0;
|
|
|
|
buf->st_gid = 0;
|
|
|
|
buf->st_uid = 0;
|
2008-08-18 20:01:06 +00:00
|
|
|
buf->st_nlink = 1;
|
2008-09-27 08:43:01 +00:00
|
|
|
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
|
2009-03-05 16:05:12 +00:00
|
|
|
buf->st_size = fdata.nFileSizeLow |
|
|
|
|
(((off_t)fdata.nFileSizeHigh)<<32);
|
2008-09-27 08:39:45 +00:00
|
|
|
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
|
2018-10-23 10:23:22 +00:00
|
|
|
filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
|
|
|
|
filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
|
|
|
|
filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
|
2010-03-17 15:17:34 +00:00
|
|
|
if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
|
2012-03-15 17:21:28 +00:00
|
|
|
WIN32_FIND_DATAW findbuf;
|
|
|
|
HANDLE handle = FindFirstFileW(wfilename, &findbuf);
|
2010-03-17 15:17:34 +00:00
|
|
|
if (handle != INVALID_HANDLE_VALUE) {
|
|
|
|
if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
|
|
|
|
(findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
|
|
|
|
if (follow) {
|
|
|
|
char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
|
|
|
|
buf->st_size = readlink(file_name, buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
|
|
|
|
} else {
|
|
|
|
buf->st_mode = S_IFLNK;
|
|
|
|
}
|
|
|
|
buf->st_mode |= S_IREAD;
|
|
|
|
if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
|
|
|
|
buf->st_mode |= S_IWRITE;
|
|
|
|
}
|
|
|
|
FindClose(handle);
|
|
|
|
}
|
|
|
|
}
|
2007-09-03 18:40:26 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2012-03-15 17:21:28 +00:00
|
|
|
switch (GetLastError()) {
|
|
|
|
case ERROR_ACCESS_DENIED:
|
|
|
|
case ERROR_SHARING_VIOLATION:
|
|
|
|
case ERROR_LOCK_VIOLATION:
|
|
|
|
case ERROR_SHARING_BUFFER_EXCEEDED:
|
|
|
|
errno = EACCES;
|
|
|
|
break;
|
|
|
|
case ERROR_BUFFER_OVERFLOW:
|
|
|
|
errno = ENAMETOOLONG;
|
|
|
|
break;
|
|
|
|
case ERROR_NOT_ENOUGH_MEMORY:
|
|
|
|
errno = ENOMEM;
|
|
|
|
break;
|
2016-01-26 14:34:52 +00:00
|
|
|
case ERROR_PATH_NOT_FOUND:
|
|
|
|
if (!has_valid_directory_prefix(wfilename)) {
|
|
|
|
errno = ENOTDIR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* fallthru */
|
2012-03-15 17:21:28 +00:00
|
|
|
default:
|
|
|
|
errno = ENOENT;
|
|
|
|
break;
|
|
|
|
}
|
2007-09-03 18:40:26 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We provide our own lstat/fstat functions, since the provided
|
|
|
|
* lstat/fstat functions are so slow. These stat functions are
|
|
|
|
* tailored for Git's usage (read: fast), and are not meant to be
|
|
|
|
* complete. Note that Git stat()s are redirected to mingw_lstat()
|
|
|
|
* too, since Windows doesn't really handle symlinks that well.
|
|
|
|
*/
|
2010-03-17 15:17:34 +00:00
|
|
|
static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
|
2007-09-03 18:40:26 +00:00
|
|
|
{
|
|
|
|
int namelen;
|
2011-01-07 17:04:16 +00:00
|
|
|
char alt_name[PATH_MAX];
|
2007-09-03 18:40:26 +00:00
|
|
|
|
2010-03-17 15:17:34 +00:00
|
|
|
if (!do_lstat(follow, file_name, buf))
|
2007-09-03 18:40:26 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* if file_name ended in a '/', Windows returned ENOENT;
|
|
|
|
* try again without trailing slashes
|
|
|
|
*/
|
|
|
|
if (errno != ENOENT)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
namelen = strlen(file_name);
|
|
|
|
if (namelen && file_name[namelen-1] != '/')
|
|
|
|
return -1;
|
|
|
|
while (namelen && file_name[namelen-1] == '/')
|
|
|
|
--namelen;
|
|
|
|
if (!namelen || namelen >= PATH_MAX)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
memcpy(alt_name, file_name, namelen);
|
|
|
|
alt_name[namelen] = 0;
|
2010-03-17 15:17:34 +00:00
|
|
|
return do_lstat(follow, alt_name, buf);
|
|
|
|
}
|
|
|
|
|
2018-10-23 10:23:19 +00:00
|
|
|
static int get_file_info_by_handle(HANDLE hnd, struct stat *buf)
|
|
|
|
{
|
|
|
|
BY_HANDLE_FILE_INFORMATION fdata;
|
|
|
|
|
|
|
|
if (!GetFileInformationByHandle(hnd, &fdata)) {
|
|
|
|
errno = err_win_to_posix(GetLastError());
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf->st_ino = 0;
|
|
|
|
buf->st_gid = 0;
|
|
|
|
buf->st_uid = 0;
|
|
|
|
buf->st_nlink = 1;
|
|
|
|
buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
|
|
|
|
buf->st_size = fdata.nFileSizeLow |
|
|
|
|
(((off_t)fdata.nFileSizeHigh)<<32);
|
|
|
|
buf->st_dev = buf->st_rdev = 0; /* not used by Git */
|
2018-10-23 10:23:22 +00:00
|
|
|
filetime_to_timespec(&(fdata.ftLastAccessTime), &(buf->st_atim));
|
|
|
|
filetime_to_timespec(&(fdata.ftLastWriteTime), &(buf->st_mtim));
|
|
|
|
filetime_to_timespec(&(fdata.ftCreationTime), &(buf->st_ctim));
|
2018-10-23 10:23:19 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-03-17 15:17:34 +00:00
|
|
|
int mingw_lstat(const char *file_name, struct stat *buf)
|
|
|
|
{
|
|
|
|
return do_stat_internal(0, file_name, buf);
|
|
|
|
}
|
|
|
|
int mingw_stat(const char *file_name, struct stat *buf)
|
|
|
|
{
|
|
|
|
return do_stat_internal(1, file_name, buf);
|
2007-09-03 18:40:26 +00:00
|
|
|
}
|
|
|
|
|
2008-08-18 20:01:06 +00:00
|
|
|
int mingw_fstat(int fd, struct stat *buf)
|
2007-09-03 18:40:26 +00:00
|
|
|
{
|
|
|
|
HANDLE fh = (HANDLE)_get_osfhandle(fd);
|
2018-10-23 10:23:21 +00:00
|
|
|
DWORD avail, type = GetFileType(fh) & ~FILE_TYPE_REMOTE;
|
2007-09-03 18:40:26 +00:00
|
|
|
|
2018-10-23 10:23:21 +00:00
|
|
|
switch (type) {
|
|
|
|
case FILE_TYPE_DISK:
|
|
|
|
return get_file_info_by_handle(fh, buf);
|
2007-09-03 18:40:26 +00:00
|
|
|
|
2018-10-23 10:23:21 +00:00
|
|
|
case FILE_TYPE_CHAR:
|
|
|
|
case FILE_TYPE_PIPE:
|
|
|
|
/* initialize stat fields */
|
|
|
|
memset(buf, 0, sizeof(*buf));
|
2008-08-18 20:01:06 +00:00
|
|
|
buf->st_nlink = 1;
|
2018-10-23 10:23:21 +00:00
|
|
|
|
|
|
|
if (type == FILE_TYPE_CHAR) {
|
|
|
|
buf->st_mode = _S_IFCHR;
|
|
|
|
} else {
|
|
|
|
buf->st_mode = _S_IFIFO;
|
|
|
|
if (PeekNamedPipe(fh, NULL, 0, NULL, &avail, NULL))
|
|
|
|
buf->st_size = avail;
|
|
|
|
}
|
2007-09-03 18:40:26 +00:00
|
|
|
return 0;
|
2018-10-23 10:23:19 +00:00
|
|
|
|
2018-10-23 10:23:21 +00:00
|
|
|
default:
|
|
|
|
errno = EBADF;
|
|
|
|
return -1;
|
2007-09-03 18:40:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-07 11:05:00 +00:00
|
|
|
static inline void time_t_to_filetime(time_t t, FILETIME *ft)
|
|
|
|
{
|
|
|
|
long long winTime = t * 10000000LL + 116444736000000000LL;
|
|
|
|
ft->dwLowDateTime = winTime;
|
|
|
|
ft->dwHighDateTime = winTime >> 32;
|
|
|
|
}
|
|
|
|
|
|
|
|
int mingw_utime (const char *file_name, const struct utimbuf *times)
|
|
|
|
{
|
|
|
|
FILETIME mft, aft;
|
2022-03-02 06:05:23 +00:00
|
|
|
int rc;
|
2012-03-15 17:21:28 +00:00
|
|
|
DWORD attrs;
|
|
|
|
wchar_t wfilename[MAX_PATH];
|
2022-03-02 06:05:23 +00:00
|
|
|
HANDLE osfilehandle;
|
|
|
|
|
2012-03-15 17:21:28 +00:00
|
|
|
if (xutftowcs_path(wfilename, file_name) < 0)
|
|
|
|
return -1;
|
2007-09-07 11:05:00 +00:00
|
|
|
|
|
|
|
/* must have write permission */
|
2012-03-15 17:21:28 +00:00
|
|
|
attrs = GetFileAttributesW(wfilename);
|
2010-03-30 07:46:23 +00:00
|
|
|
if (attrs != INVALID_FILE_ATTRIBUTES &&
|
|
|
|
(attrs & FILE_ATTRIBUTE_READONLY)) {
|
|
|
|
/* ignore errors here; open() will report them */
|
2012-03-15 17:21:28 +00:00
|
|
|
SetFileAttributesW(wfilename, attrs & ~FILE_ATTRIBUTE_READONLY);
|
2010-03-30 07:46:23 +00:00
|
|
|
}
|
|
|
|
|
2022-03-02 06:05:23 +00:00
|
|
|
osfilehandle = CreateFileW(wfilename,
|
|
|
|
FILE_WRITE_ATTRIBUTES,
|
|
|
|
0 /*FileShare.None*/,
|
|
|
|
NULL,
|
|
|
|
OPEN_EXISTING,
|
|
|
|
(attrs != INVALID_FILE_ATTRIBUTES &&
|
|
|
|
(attrs & FILE_ATTRIBUTE_DIRECTORY)) ?
|
|
|
|
FILE_FLAG_BACKUP_SEMANTICS : 0,
|
|
|
|
NULL);
|
|
|
|
if (osfilehandle == INVALID_HANDLE_VALUE) {
|
|
|
|
errno = err_win_to_posix(GetLastError());
|
2010-03-30 07:46:23 +00:00
|
|
|
rc = -1;
|
|
|
|
goto revert_attrs;
|
|
|
|
}
|
2007-09-07 11:05:00 +00:00
|
|
|
|
2010-07-12 23:42:03 +00:00
|
|
|
if (times) {
|
|
|
|
time_t_to_filetime(times->modtime, &mft);
|
|
|
|
time_t_to_filetime(times->actime, &aft);
|
|
|
|
} else {
|
|
|
|
GetSystemTimeAsFileTime(&mft);
|
|
|
|
aft = mft;
|
|
|
|
}
|
2022-03-02 06:05:23 +00:00
|
|
|
|
|
|
|
if (!SetFileTime(osfilehandle, NULL, &aft, &mft)) {
|
2007-09-07 11:05:00 +00:00
|
|
|
errno = EINVAL;
|
|
|
|
rc = -1;
|
|
|
|
} else
|
|
|
|
rc = 0;
|
2022-03-02 06:05:23 +00:00
|
|
|
|
|
|
|
if (osfilehandle != INVALID_HANDLE_VALUE)
|
|
|
|
CloseHandle(osfilehandle);
|
2010-03-30 07:46:23 +00:00
|
|
|
|
|
|
|
revert_attrs:
|
|
|
|
if (attrs != INVALID_FILE_ATTRIBUTES &&
|
|
|
|
(attrs & FILE_ATTRIBUTE_READONLY)) {
|
|
|
|
/* ignore errors again */
|
2012-03-15 17:21:28 +00:00
|
|
|
SetFileAttributesW(wfilename, attrs);
|
2010-03-30 07:46:23 +00:00
|
|
|
}
|
2007-09-07 11:05:00 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
mingw: abort on invalid strftime formats
On Windows, strftime() does not silently ignore invalid formats, but
warns about them and then returns 0 and sets errno to EINVAL.
Unfortunately, Git does not expect such a behavior, as it disagrees
with strftime()'s semantics on Linux. As a consequence, Git
misinterprets the return value 0 as "I need more space" and grows the
buffer. As the larger buffer does not fix the format, the buffer grows
and grows and grows until we are out of memory and abort.
Ideally, we would switch off the parameter validation just for
strftime(), but we cannot even override the invalid parameter handler
via _set_thread_local_invalid_parameter_handler() using MINGW because
that function is not declared. Even _set_invalid_parameter_handler(),
which *is* declared, does not help, as it simply does... nothing.
So let's just bite the bullet and override strftime() for MINGW and
abort on an invalid format string. While this does not provide the
best user experience, it is the best we can do.
See https://msdn.microsoft.com/en-us/library/fe06s4ak.aspx for more
details.
This fixes https://github.com/git-for-windows/git/issues/863
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-03-19 16:49:22 +00:00
|
|
|
#undef strftime
|
|
|
|
size_t mingw_strftime(char *s, size_t max,
|
|
|
|
const char *format, const struct tm *tm)
|
|
|
|
{
|
2020-04-08 17:58:49 +00:00
|
|
|
/* 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;
|
2022-01-08 16:02:30 +00:00
|
|
|
DECLARE_PROC_ADDR(ucrtbase.dll, size_t, __cdecl, strftime, char *, size_t,
|
2020-04-08 17:58:49 +00:00
|
|
|
const char *, const struct tm *);
|
|
|
|
|
|
|
|
if (INIT_PROC_ADDR(strftime))
|
|
|
|
ret = strftime(s, max, format, tm);
|
|
|
|
else
|
|
|
|
ret = fallback(s, max, format, tm);
|
mingw: abort on invalid strftime formats
On Windows, strftime() does not silently ignore invalid formats, but
warns about them and then returns 0 and sets errno to EINVAL.
Unfortunately, Git does not expect such a behavior, as it disagrees
with strftime()'s semantics on Linux. As a consequence, Git
misinterprets the return value 0 as "I need more space" and grows the
buffer. As the larger buffer does not fix the format, the buffer grows
and grows and grows until we are out of memory and abort.
Ideally, we would switch off the parameter validation just for
strftime(), but we cannot even override the invalid parameter handler
via _set_thread_local_invalid_parameter_handler() using MINGW because
that function is not declared. Even _set_invalid_parameter_handler(),
which *is* declared, does not help, as it simply does... nothing.
So let's just bite the bullet and override strftime() for MINGW and
abort on an invalid format string. While this does not provide the
best user experience, it is the best we can do.
See https://msdn.microsoft.com/en-us/library/fe06s4ak.aspx for more
details.
This fixes https://github.com/git-for-windows/git/issues/863
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-03-19 16:49:22 +00:00
|
|
|
|
|
|
|
if (!ret && errno == EINVAL)
|
|
|
|
die("invalid strftime format: '%s'", format);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2007-12-01 20:24:59 +00:00
|
|
|
unsigned int sleep (unsigned int seconds)
|
|
|
|
{
|
|
|
|
Sleep(seconds*1000);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-15 17:21:28 +00:00
|
|
|
char *mingw_mktemp(char *template)
|
|
|
|
{
|
|
|
|
wchar_t wtemplate[MAX_PATH];
|
|
|
|
if (xutftowcs_path(wtemplate, template) < 0)
|
|
|
|
return NULL;
|
|
|
|
if (!_wmktemp(wtemplate))
|
|
|
|
return NULL;
|
|
|
|
if (xwcstoutf(template, wtemplate, strlen(template) + 1) < 0)
|
|
|
|
return NULL;
|
|
|
|
return template;
|
|
|
|
}
|
|
|
|
|
2007-12-01 20:24:59 +00:00
|
|
|
int mkstemp(char *template)
|
|
|
|
{
|
|
|
|
char *filename = mktemp(template);
|
|
|
|
if (filename == NULL)
|
|
|
|
return -1;
|
|
|
|
return open(filename, O_RDWR | O_CREAT, 0600);
|
|
|
|
}
|
|
|
|
|
|
|
|
int gettimeofday(struct timeval *tv, void *tz)
|
|
|
|
{
|
2010-01-15 20:12:21 +00:00
|
|
|
FILETIME ft;
|
|
|
|
long long hnsec;
|
|
|
|
|
|
|
|
GetSystemTimeAsFileTime(&ft);
|
|
|
|
hnsec = filetime_to_hnsec(&ft);
|
|
|
|
tv->tv_sec = hnsec / 10000000;
|
|
|
|
tv->tv_usec = (hnsec % 10000000) / 10;
|
2007-12-01 20:51:20 +00:00
|
|
|
return 0;
|
2007-12-01 20:24:59 +00:00
|
|
|
}
|
|
|
|
|
Windows: A pipe() replacement whose ends are not inherited to children.
On Unix the idiom to use a pipe is as follows:
pipe(fd);
pid = fork();
if (!pid) {
dup2(fd[1], 1);
close(fd[1]);
close(fd[0]);
...
}
close(fd[1]);
i.e. the child process closes the both pipe ends after duplicating one
to the file descriptors where they are needed.
On Windows, which does not have fork(), we never have an opportunity to
(1) duplicate a pipe end in the child, (2) close unused pipe ends. Instead,
we must use this idiom:
save1 = dup(1);
pipe(fd);
dup2(fd[1], 1);
spawn(...);
dup2(save1, 1);
close(fd[1]);
i.e. save away the descriptor at the destination slot, replace by the pipe
end, spawn process, restore the saved file.
But there is a problem: Notice that the child did not only inherit the
dup2()ed descriptor, but also *both* original pipe ends. Although the one
end that was dup()ed could be closed before the spawn(), we cannot close
the other end - the child inherits it, no matter what.
The solution is to generate non-inheritable pipes. At the first glance,
this looks strange: The purpose of pipes is usually to be inherited to
child processes. But notice that in the course of actions as outlined
above, the pipe descriptor that we want to inherit to the child is
dup2()ed, and as it so happens, Windows's dup2() creates inheritable
duplicates.
Signed-off-by: Johannes Sixt <johannes.sixt@telecom.at>
2007-12-07 21:05:36 +00:00
|
|
|
int pipe(int filedes[2])
|
|
|
|
{
|
2010-01-15 20:12:17 +00:00
|
|
|
HANDLE h[2];
|
Windows: A pipe() replacement whose ends are not inherited to children.
On Unix the idiom to use a pipe is as follows:
pipe(fd);
pid = fork();
if (!pid) {
dup2(fd[1], 1);
close(fd[1]);
close(fd[0]);
...
}
close(fd[1]);
i.e. the child process closes the both pipe ends after duplicating one
to the file descriptors where they are needed.
On Windows, which does not have fork(), we never have an opportunity to
(1) duplicate a pipe end in the child, (2) close unused pipe ends. Instead,
we must use this idiom:
save1 = dup(1);
pipe(fd);
dup2(fd[1], 1);
spawn(...);
dup2(save1, 1);
close(fd[1]);
i.e. save away the descriptor at the destination slot, replace by the pipe
end, spawn process, restore the saved file.
But there is a problem: Notice that the child did not only inherit the
dup2()ed descriptor, but also *both* original pipe ends. Although the one
end that was dup()ed could be closed before the spawn(), we cannot close
the other end - the child inherits it, no matter what.
The solution is to generate non-inheritable pipes. At the first glance,
this looks strange: The purpose of pipes is usually to be inherited to
child processes. But notice that in the course of actions as outlined
above, the pipe descriptor that we want to inherit to the child is
dup2()ed, and as it so happens, Windows's dup2() creates inheritable
duplicates.
Signed-off-by: Johannes Sixt <johannes.sixt@telecom.at>
2007-12-07 21:05:36 +00:00
|
|
|
|
2010-01-15 20:12:17 +00:00
|
|
|
/* this creates non-inheritable handles */
|
|
|
|
if (!CreatePipe(&h[0], &h[1], NULL, 8192)) {
|
|
|
|
errno = err_win_to_posix(GetLastError());
|
Windows: A pipe() replacement whose ends are not inherited to children.
On Unix the idiom to use a pipe is as follows:
pipe(fd);
pid = fork();
if (!pid) {
dup2(fd[1], 1);
close(fd[1]);
close(fd[0]);
...
}
close(fd[1]);
i.e. the child process closes the both pipe ends after duplicating one
to the file descriptors where they are needed.
On Windows, which does not have fork(), we never have an opportunity to
(1) duplicate a pipe end in the child, (2) close unused pipe ends. Instead,
we must use this idiom:
save1 = dup(1);
pipe(fd);
dup2(fd[1], 1);
spawn(...);
dup2(save1, 1);
close(fd[1]);
i.e. save away the descriptor at the destination slot, replace by the pipe
end, spawn process, restore the saved file.
But there is a problem: Notice that the child did not only inherit the
dup2()ed descriptor, but also *both* original pipe ends. Although the one
end that was dup()ed could be closed before the spawn(), we cannot close
the other end - the child inherits it, no matter what.
The solution is to generate non-inheritable pipes. At the first glance,
this looks strange: The purpose of pipes is usually to be inherited to
child processes. But notice that in the course of actions as outlined
above, the pipe descriptor that we want to inherit to the child is
dup2()ed, and as it so happens, Windows's dup2() creates inheritable
duplicates.
Signed-off-by: Johannes Sixt <johannes.sixt@telecom.at>
2007-12-07 21:05:36 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2016-01-15 13:24:34 +00:00
|
|
|
filedes[0] = _open_osfhandle(HCAST(int, h[0]), O_NOINHERIT);
|
2010-01-15 20:12:17 +00:00
|
|
|
if (filedes[0] < 0) {
|
Windows: A pipe() replacement whose ends are not inherited to children.
On Unix the idiom to use a pipe is as follows:
pipe(fd);
pid = fork();
if (!pid) {
dup2(fd[1], 1);
close(fd[1]);
close(fd[0]);
...
}
close(fd[1]);
i.e. the child process closes the both pipe ends after duplicating one
to the file descriptors where they are needed.
On Windows, which does not have fork(), we never have an opportunity to
(1) duplicate a pipe end in the child, (2) close unused pipe ends. Instead,
we must use this idiom:
save1 = dup(1);
pipe(fd);
dup2(fd[1], 1);
spawn(...);
dup2(save1, 1);
close(fd[1]);
i.e. save away the descriptor at the destination slot, replace by the pipe
end, spawn process, restore the saved file.
But there is a problem: Notice that the child did not only inherit the
dup2()ed descriptor, but also *both* original pipe ends. Although the one
end that was dup()ed could be closed before the spawn(), we cannot close
the other end - the child inherits it, no matter what.
The solution is to generate non-inheritable pipes. At the first glance,
this looks strange: The purpose of pipes is usually to be inherited to
child processes. But notice that in the course of actions as outlined
above, the pipe descriptor that we want to inherit to the child is
dup2()ed, and as it so happens, Windows's dup2() creates inheritable
duplicates.
Signed-off-by: Johannes Sixt <johannes.sixt@telecom.at>
2007-12-07 21:05:36 +00:00
|
|
|
CloseHandle(h[0]);
|
|
|
|
CloseHandle(h[1]);
|
|
|
|
return -1;
|
|
|
|
}
|
2016-01-15 13:24:34 +00:00
|
|
|
filedes[1] = _open_osfhandle(HCAST(int, h[1]), O_NOINHERIT);
|
2015-08-28 09:43:37 +00:00
|
|
|
if (filedes[1] < 0) {
|
Windows: A pipe() replacement whose ends are not inherited to children.
On Unix the idiom to use a pipe is as follows:
pipe(fd);
pid = fork();
if (!pid) {
dup2(fd[1], 1);
close(fd[1]);
close(fd[0]);
...
}
close(fd[1]);
i.e. the child process closes the both pipe ends after duplicating one
to the file descriptors where they are needed.
On Windows, which does not have fork(), we never have an opportunity to
(1) duplicate a pipe end in the child, (2) close unused pipe ends. Instead,
we must use this idiom:
save1 = dup(1);
pipe(fd);
dup2(fd[1], 1);
spawn(...);
dup2(save1, 1);
close(fd[1]);
i.e. save away the descriptor at the destination slot, replace by the pipe
end, spawn process, restore the saved file.
But there is a problem: Notice that the child did not only inherit the
dup2()ed descriptor, but also *both* original pipe ends. Although the one
end that was dup()ed could be closed before the spawn(), we cannot close
the other end - the child inherits it, no matter what.
The solution is to generate non-inheritable pipes. At the first glance,
this looks strange: The purpose of pipes is usually to be inherited to
child processes. But notice that in the course of actions as outlined
above, the pipe descriptor that we want to inherit to the child is
dup2()ed, and as it so happens, Windows's dup2() creates inheritable
duplicates.
Signed-off-by: Johannes Sixt <johannes.sixt@telecom.at>
2007-12-07 21:05:36 +00:00
|
|
|
close(filedes[0]);
|
|
|
|
CloseHandle(h[1]);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-11-27 10:15:32 +00:00
|
|
|
#ifndef __MINGW64__
|
2007-12-01 20:24:59 +00:00
|
|
|
struct tm *gmtime_r(const time_t *timep, struct tm *result)
|
|
|
|
{
|
mingw: use {gm,local}time_s as backend for {gm,local}time_r
Since Windows doesn't provide gmtime_r(3) and localtime_r(3),
we're providing a compat version by using non-reentrant gmtime(3) and
localtime(3) as backend. Then, we copy the returned data into the
buffer.
By doing that, in case of failure, we will dereference a NULL pointer
returned by gmtime(3), and localtime(3), and we always return a valid
pointer instead of NULL.
Drop the memcpy(3) by using gmtime_s(), and use localtime_s() as the
backend on Windows, and make sure we will return NULL in case of
failure.
Cc: Johannes Sixt <j6t@kdbg.org>
Cc: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Doan Tran Cong Danh <congdanhqx@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-11-28 12:25:05 +00:00
|
|
|
if (gmtime_s(result, timep) == 0)
|
|
|
|
return result;
|
|
|
|
return NULL;
|
2007-12-01 20:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct tm *localtime_r(const time_t *timep, struct tm *result)
|
|
|
|
{
|
mingw: use {gm,local}time_s as backend for {gm,local}time_r
Since Windows doesn't provide gmtime_r(3) and localtime_r(3),
we're providing a compat version by using non-reentrant gmtime(3) and
localtime(3) as backend. Then, we copy the returned data into the
buffer.
By doing that, in case of failure, we will dereference a NULL pointer
returned by gmtime(3), and localtime(3), and we always return a valid
pointer instead of NULL.
Drop the memcpy(3) by using gmtime_s(), and use localtime_s() as the
backend on Windows, and make sure we will return NULL in case of
failure.
Cc: Johannes Sixt <j6t@kdbg.org>
Cc: Johannes Schindelin <Johannes.Schindelin@gmx.de>
Signed-off-by: Doan Tran Cong Danh <congdanhqx@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-11-28 12:25:05 +00:00
|
|
|
if (localtime_s(result, timep) == 0)
|
|
|
|
return result;
|
|
|
|
return NULL;
|
2007-12-01 20:24:59 +00:00
|
|
|
}
|
2021-11-27 10:15:32 +00:00
|
|
|
#endif
|
2007-12-01 20:24:59 +00:00
|
|
|
|
2008-03-05 20:51:27 +00:00
|
|
|
char *mingw_getcwd(char *pointer, int len)
|
|
|
|
{
|
2018-10-23 10:52:48 +00:00
|
|
|
wchar_t cwd[MAX_PATH], wpointer[MAX_PATH];
|
|
|
|
DWORD ret = GetCurrentDirectoryW(ARRAY_SIZE(cwd), cwd);
|
|
|
|
|
|
|
|
if (!ret || ret >= ARRAY_SIZE(cwd)) {
|
|
|
|
errno = ret ? ENAMETOOLONG : err_win_to_posix(GetLastError());
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
ret = GetLongPathNameW(cwd, wpointer, ARRAY_SIZE(wpointer));
|
2018-10-23 10:52:49 +00:00
|
|
|
if (!ret && GetLastError() == ERROR_ACCESS_DENIED) {
|
|
|
|
HANDLE hnd = CreateFileW(cwd, 0,
|
|
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
|
|
|
|
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
|
|
|
if (hnd == INVALID_HANDLE_VALUE)
|
|
|
|
return NULL;
|
|
|
|
ret = GetFinalPathNameByHandleW(hnd, wpointer, ARRAY_SIZE(wpointer), 0);
|
|
|
|
CloseHandle(hnd);
|
|
|
|
if (!ret || ret >= ARRAY_SIZE(wpointer))
|
|
|
|
return NULL;
|
|
|
|
if (xwcstoutf(pointer, normalize_ntpath(wpointer), len) < 0)
|
|
|
|
return NULL;
|
|
|
|
return pointer;
|
|
|
|
}
|
2018-10-23 10:52:48 +00:00
|
|
|
if (!ret || ret >= ARRAY_SIZE(wpointer))
|
2012-03-15 17:21:28 +00:00
|
|
|
return NULL;
|
2022-01-19 18:56:01 +00:00
|
|
|
if (GetFileAttributesW(wpointer) == INVALID_FILE_ATTRIBUTES) {
|
|
|
|
errno = ENOENT;
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-03-15 17:21:28 +00:00
|
|
|
if (xwcstoutf(pointer, wpointer, len) < 0)
|
|
|
|
return NULL;
|
2016-04-02 19:03:14 +00:00
|
|
|
convert_slashes(pointer);
|
2012-03-15 17:21:28 +00:00
|
|
|
return pointer;
|
2008-03-05 20:51:27 +00:00
|
|
|
}
|
|
|
|
|
2007-11-24 21:49:16 +00:00
|
|
|
/*
|
2018-11-15 11:22:40 +00:00
|
|
|
* See "Parsing C++ Command-Line Arguments" at Microsoft's Docs:
|
|
|
|
* https://docs.microsoft.com/en-us/cpp/cpp/parsing-cpp-command-line-arguments
|
2007-11-24 21:49:16 +00:00
|
|
|
*/
|
mingw: special-case arguments to `sh`
The MSYS2 runtime does its best to emulate the command-line wildcard
expansion and de-quoting which would be performed by the calling Unix
shell on Unix systems.
Those Unix shell quoting rules differ from the quoting rules applying to
Windows' cmd and Powershell, making it a little awkward to quote
command-line parameters properly when spawning other processes.
In particular, git.exe passes arguments to subprocesses that are *not*
intended to be interpreted as wildcards, and if they contain
backslashes, those are not to be interpreted as escape characters, e.g.
when passing Windows paths.
Note: this is only a problem when calling MSYS2 executables, not when
calling MINGW executables such as git.exe. However, we do call MSYS2
executables frequently, most notably when setting the use_shell flag in
the child_process structure.
There is no elegant way to determine whether the .exe file to be
executed is an MSYS2 program or a MINGW one. But since the use case of
passing a command line through the shell is so prevalent, we need to
work around this issue at least when executing sh.exe.
Let's introduce an ugly, hard-coded test whether argv[0] is "sh", and
whether it refers to the MSYS2 Bash, to determine whether we need to
quote the arguments differently than usual.
That still does not fix the issue completely, but at least it is
something.
Incidentally, this also fixes the problem where `git clone \\server\repo`
failed due to incorrect handling of the backslashes when handing the path
to the git-upload-pack process.
Further, we need to take care to quote not only whitespace and
backslashes, but also curly brackets. As aliases frequently go through
the MSYS2 Bash, and as aliases frequently get parameters such as
HEAD@{yesterday}, this is really important. As an early version of this
patch broke this, let's make sure that this does not regress by adding a
test case for that.
Helped-by: Kim Gybels <kgybels@infogroep.be>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-01-17 20:14:48 +00:00
|
|
|
static const char *quote_arg_msvc(const char *arg)
|
2007-11-24 21:49:16 +00:00
|
|
|
{
|
|
|
|
/* count chars to quote */
|
|
|
|
int len = 0, n = 0;
|
|
|
|
int force_quotes = 0;
|
|
|
|
char *q, *d;
|
|
|
|
const char *p = arg;
|
|
|
|
if (!*p) force_quotes = 1;
|
|
|
|
while (*p) {
|
2009-03-24 20:43:02 +00:00
|
|
|
if (isspace(*p) || *p == '*' || *p == '?' || *p == '{' || *p == '\'')
|
2007-11-24 21:49:16 +00:00
|
|
|
force_quotes = 1;
|
|
|
|
else if (*p == '"')
|
|
|
|
n++;
|
|
|
|
else if (*p == '\\') {
|
|
|
|
int count = 0;
|
|
|
|
while (*p == '\\') {
|
|
|
|
count++;
|
|
|
|
p++;
|
|
|
|
len++;
|
|
|
|
}
|
mingw: fix quoting of arguments
We need to be careful to follow proper quoting rules. For example, if an
argument contains spaces, we have to quote them. Double-quotes need to
be escaped. Backslashes need to be escaped, but only if they are
followed by a double-quote character.
We need to be _extra_ careful to consider the case where an argument
ends in a backslash _and_ needs to be quoted: in this case, we append a
double-quote character, i.e. the backslash now has to be escaped!
The current code, however, fails to recognize that, and therefore can
turn an argument that ends in a single backslash into a quoted argument
that now ends in an escaped double-quote character. This allows
subsequent command-line parameters to be split and part of them being
mistaken for command-line options, e.g. through a maliciously-crafted
submodule URL during a recursive clone.
Technically, we would not need to quote _all_ arguments which end in a
backslash _unless_ the argument needs to be quoted anyway. For example,
`test\` would not need to be quoted, while `test \` would need to be.
To keep the code simple, however, and therefore easier to reason about
and ensure its correctness, we now _always_ quote an argument that ends
in a backslash.
This addresses CVE-2019-1350.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-13 14:32:43 +00:00
|
|
|
if (*p == '"' || !*p)
|
2007-11-24 21:49:16 +00:00
|
|
|
n += count*2 + 1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
len++;
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
if (!force_quotes && n == 0)
|
|
|
|
return arg;
|
|
|
|
|
|
|
|
/* insert \ where necessary */
|
2016-02-22 22:44:35 +00:00
|
|
|
d = q = xmalloc(st_add3(len, n, 3));
|
2007-11-24 21:49:16 +00:00
|
|
|
*d++ = '"';
|
|
|
|
while (*arg) {
|
|
|
|
if (*arg == '"')
|
|
|
|
*d++ = '\\';
|
|
|
|
else if (*arg == '\\') {
|
|
|
|
int count = 0;
|
|
|
|
while (*arg == '\\') {
|
|
|
|
count++;
|
|
|
|
*d++ = *arg++;
|
|
|
|
}
|
mingw: fix quoting of arguments
We need to be careful to follow proper quoting rules. For example, if an
argument contains spaces, we have to quote them. Double-quotes need to
be escaped. Backslashes need to be escaped, but only if they are
followed by a double-quote character.
We need to be _extra_ careful to consider the case where an argument
ends in a backslash _and_ needs to be quoted: in this case, we append a
double-quote character, i.e. the backslash now has to be escaped!
The current code, however, fails to recognize that, and therefore can
turn an argument that ends in a single backslash into a quoted argument
that now ends in an escaped double-quote character. This allows
subsequent command-line parameters to be split and part of them being
mistaken for command-line options, e.g. through a maliciously-crafted
submodule URL during a recursive clone.
Technically, we would not need to quote _all_ arguments which end in a
backslash _unless_ the argument needs to be quoted anyway. For example,
`test\` would not need to be quoted, while `test \` would need to be.
To keep the code simple, however, and therefore easier to reason about
and ensure its correctness, we now _always_ quote an argument that ends
in a backslash.
This addresses CVE-2019-1350.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-13 14:32:43 +00:00
|
|
|
if (*arg == '"' || !*arg) {
|
2007-11-24 21:49:16 +00:00
|
|
|
while (count-- > 0)
|
|
|
|
*d++ = '\\';
|
mingw: fix quoting of arguments
We need to be careful to follow proper quoting rules. For example, if an
argument contains spaces, we have to quote them. Double-quotes need to
be escaped. Backslashes need to be escaped, but only if they are
followed by a double-quote character.
We need to be _extra_ careful to consider the case where an argument
ends in a backslash _and_ needs to be quoted: in this case, we append a
double-quote character, i.e. the backslash now has to be escaped!
The current code, however, fails to recognize that, and therefore can
turn an argument that ends in a single backslash into a quoted argument
that now ends in an escaped double-quote character. This allows
subsequent command-line parameters to be split and part of them being
mistaken for command-line options, e.g. through a maliciously-crafted
submodule URL during a recursive clone.
Technically, we would not need to quote _all_ arguments which end in a
backslash _unless_ the argument needs to be quoted anyway. For example,
`test\` would not need to be quoted, while `test \` would need to be.
To keep the code simple, however, and therefore easier to reason about
and ensure its correctness, we now _always_ quote an argument that ends
in a backslash.
This addresses CVE-2019-1350.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-13 14:32:43 +00:00
|
|
|
/* don't escape the surrounding end quote */
|
|
|
|
if (!*arg)
|
|
|
|
break;
|
2007-11-24 21:49:16 +00:00
|
|
|
*d++ = '\\';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*d++ = *arg++;
|
|
|
|
}
|
|
|
|
*d++ = '"';
|
mingw: fix quoting of arguments
We need to be careful to follow proper quoting rules. For example, if an
argument contains spaces, we have to quote them. Double-quotes need to
be escaped. Backslashes need to be escaped, but only if they are
followed by a double-quote character.
We need to be _extra_ careful to consider the case where an argument
ends in a backslash _and_ needs to be quoted: in this case, we append a
double-quote character, i.e. the backslash now has to be escaped!
The current code, however, fails to recognize that, and therefore can
turn an argument that ends in a single backslash into a quoted argument
that now ends in an escaped double-quote character. This allows
subsequent command-line parameters to be split and part of them being
mistaken for command-line options, e.g. through a maliciously-crafted
submodule URL during a recursive clone.
Technically, we would not need to quote _all_ arguments which end in a
backslash _unless_ the argument needs to be quoted anyway. For example,
`test\` would not need to be quoted, while `test \` would need to be.
To keep the code simple, however, and therefore easier to reason about
and ensure its correctness, we now _always_ quote an argument that ends
in a backslash.
This addresses CVE-2019-1350.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-13 14:32:43 +00:00
|
|
|
*d++ = '\0';
|
2007-11-24 21:49:16 +00:00
|
|
|
return q;
|
|
|
|
}
|
|
|
|
|
mingw: special-case arguments to `sh`
The MSYS2 runtime does its best to emulate the command-line wildcard
expansion and de-quoting which would be performed by the calling Unix
shell on Unix systems.
Those Unix shell quoting rules differ from the quoting rules applying to
Windows' cmd and Powershell, making it a little awkward to quote
command-line parameters properly when spawning other processes.
In particular, git.exe passes arguments to subprocesses that are *not*
intended to be interpreted as wildcards, and if they contain
backslashes, those are not to be interpreted as escape characters, e.g.
when passing Windows paths.
Note: this is only a problem when calling MSYS2 executables, not when
calling MINGW executables such as git.exe. However, we do call MSYS2
executables frequently, most notably when setting the use_shell flag in
the child_process structure.
There is no elegant way to determine whether the .exe file to be
executed is an MSYS2 program or a MINGW one. But since the use case of
passing a command line through the shell is so prevalent, we need to
work around this issue at least when executing sh.exe.
Let's introduce an ugly, hard-coded test whether argv[0] is "sh", and
whether it refers to the MSYS2 Bash, to determine whether we need to
quote the arguments differently than usual.
That still does not fix the issue completely, but at least it is
something.
Incidentally, this also fixes the problem where `git clone \\server\repo`
failed due to incorrect handling of the backslashes when handing the path
to the git-upload-pack process.
Further, we need to take care to quote not only whitespace and
backslashes, but also curly brackets. As aliases frequently go through
the MSYS2 Bash, and as aliases frequently get parameters such as
HEAD@{yesterday}, this is really important. As an early version of this
patch broke this, let's make sure that this does not regress by adding a
test case for that.
Helped-by: Kim Gybels <kgybels@infogroep.be>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-01-17 20:14:48 +00:00
|
|
|
#include "quote.h"
|
|
|
|
|
|
|
|
static const char *quote_arg_msys2(const char *arg)
|
|
|
|
{
|
|
|
|
struct strbuf buf = STRBUF_INIT;
|
|
|
|
const char *p2 = arg, *p;
|
|
|
|
|
|
|
|
for (p = arg; *p; p++) {
|
|
|
|
int ws = isspace(*p);
|
2019-09-19 21:43:03 +00:00
|
|
|
if (!ws && *p != '\\' && *p != '"' && *p != '{' && *p != '\'' &&
|
|
|
|
*p != '?' && *p != '*' && *p != '~')
|
mingw: special-case arguments to `sh`
The MSYS2 runtime does its best to emulate the command-line wildcard
expansion and de-quoting which would be performed by the calling Unix
shell on Unix systems.
Those Unix shell quoting rules differ from the quoting rules applying to
Windows' cmd and Powershell, making it a little awkward to quote
command-line parameters properly when spawning other processes.
In particular, git.exe passes arguments to subprocesses that are *not*
intended to be interpreted as wildcards, and if they contain
backslashes, those are not to be interpreted as escape characters, e.g.
when passing Windows paths.
Note: this is only a problem when calling MSYS2 executables, not when
calling MINGW executables such as git.exe. However, we do call MSYS2
executables frequently, most notably when setting the use_shell flag in
the child_process structure.
There is no elegant way to determine whether the .exe file to be
executed is an MSYS2 program or a MINGW one. But since the use case of
passing a command line through the shell is so prevalent, we need to
work around this issue at least when executing sh.exe.
Let's introduce an ugly, hard-coded test whether argv[0] is "sh", and
whether it refers to the MSYS2 Bash, to determine whether we need to
quote the arguments differently than usual.
That still does not fix the issue completely, but at least it is
something.
Incidentally, this also fixes the problem where `git clone \\server\repo`
failed due to incorrect handling of the backslashes when handing the path
to the git-upload-pack process.
Further, we need to take care to quote not only whitespace and
backslashes, but also curly brackets. As aliases frequently go through
the MSYS2 Bash, and as aliases frequently get parameters such as
HEAD@{yesterday}, this is really important. As an early version of this
patch broke this, let's make sure that this does not regress by adding a
test case for that.
Helped-by: Kim Gybels <kgybels@infogroep.be>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-01-17 20:14:48 +00:00
|
|
|
continue;
|
|
|
|
if (!buf.len)
|
|
|
|
strbuf_addch(&buf, '"');
|
|
|
|
if (p != p2)
|
|
|
|
strbuf_add(&buf, p2, p - p2);
|
2019-09-19 21:43:03 +00:00
|
|
|
if (*p == '\\' || *p == '"')
|
mingw: special-case arguments to `sh`
The MSYS2 runtime does its best to emulate the command-line wildcard
expansion and de-quoting which would be performed by the calling Unix
shell on Unix systems.
Those Unix shell quoting rules differ from the quoting rules applying to
Windows' cmd and Powershell, making it a little awkward to quote
command-line parameters properly when spawning other processes.
In particular, git.exe passes arguments to subprocesses that are *not*
intended to be interpreted as wildcards, and if they contain
backslashes, those are not to be interpreted as escape characters, e.g.
when passing Windows paths.
Note: this is only a problem when calling MSYS2 executables, not when
calling MINGW executables such as git.exe. However, we do call MSYS2
executables frequently, most notably when setting the use_shell flag in
the child_process structure.
There is no elegant way to determine whether the .exe file to be
executed is an MSYS2 program or a MINGW one. But since the use case of
passing a command line through the shell is so prevalent, we need to
work around this issue at least when executing sh.exe.
Let's introduce an ugly, hard-coded test whether argv[0] is "sh", and
whether it refers to the MSYS2 Bash, to determine whether we need to
quote the arguments differently than usual.
That still does not fix the issue completely, but at least it is
something.
Incidentally, this also fixes the problem where `git clone \\server\repo`
failed due to incorrect handling of the backslashes when handing the path
to the git-upload-pack process.
Further, we need to take care to quote not only whitespace and
backslashes, but also curly brackets. As aliases frequently go through
the MSYS2 Bash, and as aliases frequently get parameters such as
HEAD@{yesterday}, this is really important. As an early version of this
patch broke this, let's make sure that this does not regress by adding a
test case for that.
Helped-by: Kim Gybels <kgybels@infogroep.be>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-01-17 20:14:48 +00:00
|
|
|
strbuf_addch(&buf, '\\');
|
|
|
|
p2 = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (p == arg)
|
|
|
|
strbuf_addch(&buf, '"');
|
|
|
|
else if (!buf.len)
|
|
|
|
return arg;
|
|
|
|
else
|
2019-09-19 21:38:33 +00:00
|
|
|
strbuf_add(&buf, p2, p - p2);
|
mingw: special-case arguments to `sh`
The MSYS2 runtime does its best to emulate the command-line wildcard
expansion and de-quoting which would be performed by the calling Unix
shell on Unix systems.
Those Unix shell quoting rules differ from the quoting rules applying to
Windows' cmd and Powershell, making it a little awkward to quote
command-line parameters properly when spawning other processes.
In particular, git.exe passes arguments to subprocesses that are *not*
intended to be interpreted as wildcards, and if they contain
backslashes, those are not to be interpreted as escape characters, e.g.
when passing Windows paths.
Note: this is only a problem when calling MSYS2 executables, not when
calling MINGW executables such as git.exe. However, we do call MSYS2
executables frequently, most notably when setting the use_shell flag in
the child_process structure.
There is no elegant way to determine whether the .exe file to be
executed is an MSYS2 program or a MINGW one. But since the use case of
passing a command line through the shell is so prevalent, we need to
work around this issue at least when executing sh.exe.
Let's introduce an ugly, hard-coded test whether argv[0] is "sh", and
whether it refers to the MSYS2 Bash, to determine whether we need to
quote the arguments differently than usual.
That still does not fix the issue completely, but at least it is
something.
Incidentally, this also fixes the problem where `git clone \\server\repo`
failed due to incorrect handling of the backslashes when handing the path
to the git-upload-pack process.
Further, we need to take care to quote not only whitespace and
backslashes, but also curly brackets. As aliases frequently go through
the MSYS2 Bash, and as aliases frequently get parameters such as
HEAD@{yesterday}, this is really important. As an early version of this
patch broke this, let's make sure that this does not regress by adding a
test case for that.
Helped-by: Kim Gybels <kgybels@infogroep.be>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-01-17 20:14:48 +00:00
|
|
|
|
|
|
|
strbuf_addch(&buf, '"');
|
|
|
|
return strbuf_detach(&buf, 0);
|
|
|
|
}
|
|
|
|
|
2007-12-04 11:38:32 +00:00
|
|
|
static const char *parse_interpreter(const char *cmd)
|
|
|
|
{
|
|
|
|
static char buf[100];
|
|
|
|
char *p, *opt;
|
|
|
|
int n, fd;
|
|
|
|
|
|
|
|
/* don't even try a .exe */
|
|
|
|
n = strlen(cmd);
|
|
|
|
if (n >= 4 && !strcasecmp(cmd+n-4, ".exe"))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
fd = open(cmd, O_RDONLY);
|
|
|
|
if (fd < 0)
|
|
|
|
return NULL;
|
|
|
|
n = read(fd, buf, sizeof(buf)-1);
|
|
|
|
close(fd);
|
|
|
|
if (n < 4) /* at least '#!/x' and not error */
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (buf[0] != '#' || buf[1] != '!')
|
|
|
|
return NULL;
|
|
|
|
buf[n] = '\0';
|
2009-05-23 08:04:47 +00:00
|
|
|
p = buf + strcspn(buf, "\r\n");
|
|
|
|
if (!*p)
|
2007-12-04 11:38:32 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
*p = '\0';
|
|
|
|
if (!(p = strrchr(buf+2, '/')) && !(p = strrchr(buf+2, '\\')))
|
|
|
|
return NULL;
|
|
|
|
/* strip options */
|
|
|
|
if ((opt = strchr(p+1, ' ')))
|
|
|
|
*opt = '\0';
|
|
|
|
return p+1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* exe_only means that we only want to detect .exe files, but not scripts
|
|
|
|
* (which do not have an extension)
|
|
|
|
*/
|
2017-05-20 19:35:37 +00:00
|
|
|
static char *lookup_prog(const char *dir, int dirlen, const char *cmd,
|
|
|
|
int isexe, int exe_only)
|
2007-12-04 11:38:32 +00:00
|
|
|
{
|
|
|
|
char path[MAX_PATH];
|
2019-08-24 22:38:56 +00:00
|
|
|
wchar_t wpath[MAX_PATH];
|
2017-05-20 19:35:37 +00:00
|
|
|
snprintf(path, sizeof(path), "%.*s\\%s.exe", dirlen, dir, cmd);
|
2007-12-04 11:38:32 +00:00
|
|
|
|
2019-08-24 22:38:56 +00:00
|
|
|
if (xutftowcs_path(wpath, path) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!isexe && _waccess(wpath, F_OK) == 0)
|
2007-12-04 11:38:32 +00:00
|
|
|
return xstrdup(path);
|
2019-08-24 22:38:56 +00:00
|
|
|
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';
|
2008-07-18 07:34:42 +00:00
|
|
|
return xstrdup(path);
|
2019-08-24 22:38:56 +00:00
|
|
|
}
|
|
|
|
}
|
2007-12-04 11:38:32 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2010-08-22 11:12:12 +00:00
|
|
|
* Determines the absolute path of cmd using the split path in path.
|
2007-12-04 11:38:32 +00:00
|
|
|
* If cmd contains a slash or backslash, no lookup is performed.
|
|
|
|
*/
|
2017-05-20 19:35:37 +00:00
|
|
|
static char *path_lookup(const char *cmd, int exe_only)
|
2007-12-04 11:38:32 +00:00
|
|
|
{
|
2017-05-20 19:35:37 +00:00
|
|
|
const char *path;
|
2007-12-04 11:38:32 +00:00
|
|
|
char *prog = NULL;
|
|
|
|
int len = strlen(cmd);
|
|
|
|
int isexe = len >= 4 && !strcasecmp(cmd+len-4, ".exe");
|
|
|
|
|
2020-02-22 18:51:19 +00:00
|
|
|
if (strpbrk(cmd, "/\\"))
|
2017-05-20 19:35:37 +00:00
|
|
|
return xstrdup(cmd);
|
|
|
|
|
|
|
|
path = mingw_getenv("PATH");
|
|
|
|
if (!path)
|
|
|
|
return NULL;
|
2007-12-04 11:38:32 +00:00
|
|
|
|
2017-05-20 19:35:37 +00:00
|
|
|
while (!prog) {
|
|
|
|
const char *sep = strchrnul(path, ';');
|
|
|
|
int dirlen = sep - path;
|
|
|
|
if (dirlen)
|
|
|
|
prog = lookup_prog(path, dirlen, cmd, isexe, exe_only);
|
|
|
|
if (!*sep)
|
|
|
|
break;
|
|
|
|
path = sep + 1;
|
|
|
|
}
|
2007-12-04 11:38:32 +00:00
|
|
|
|
|
|
|
return prog;
|
|
|
|
}
|
|
|
|
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
static const wchar_t *wcschrnul(const wchar_t *s, wchar_t c)
|
|
|
|
{
|
|
|
|
while (*s && *s != c)
|
|
|
|
s++;
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare only keys */
|
|
|
|
static int wenvcmp(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
wchar_t *p = *(wchar_t **)a, *q = *(wchar_t **)b;
|
|
|
|
size_t p_len, q_len;
|
|
|
|
|
|
|
|
/* Find the keys */
|
|
|
|
p_len = wcschrnul(p, L'=') - p;
|
|
|
|
q_len = wcschrnul(q, L'=') - q;
|
2014-07-17 15:38:02 +00:00
|
|
|
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
/* If the length differs, include the shorter key's NUL */
|
|
|
|
if (p_len < q_len)
|
|
|
|
p_len++;
|
|
|
|
else if (p_len > q_len)
|
|
|
|
p_len = q_len + 1;
|
|
|
|
|
|
|
|
return _wcsnicmp(p, q, p_len);
|
|
|
|
}
|
2014-07-17 15:38:02 +00:00
|
|
|
|
2014-07-17 15:38:00 +00:00
|
|
|
/*
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
* Build an environment block combining the inherited environment
|
|
|
|
* merged with the given list of settings.
|
|
|
|
*
|
|
|
|
* Values of the form "KEY=VALUE" in deltaenv override inherited values.
|
|
|
|
* Values of the form "KEY" in deltaenv delete inherited values.
|
|
|
|
*
|
|
|
|
* Multiple entries in deltaenv for the same key are explicitly allowed.
|
|
|
|
*
|
|
|
|
* We return a contiguous block of UNICODE strings with a final trailing
|
|
|
|
* zero word.
|
2014-07-17 15:38:00 +00:00
|
|
|
*/
|
2014-07-17 15:38:01 +00:00
|
|
|
static wchar_t *make_environment_block(char **deltaenv)
|
2007-11-24 21:49:16 +00:00
|
|
|
{
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
wchar_t *wenv = GetEnvironmentStringsW(), *wdeltaenv, *result, *p;
|
|
|
|
size_t wlen, s, delta_size, size;
|
2014-07-17 15:38:00 +00:00
|
|
|
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
wchar_t **array = NULL;
|
|
|
|
size_t alloc = 0, nr = 0, i;
|
2014-07-17 15:38:00 +00:00
|
|
|
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
size = 1; /* for extra NUL at the end */
|
|
|
|
|
|
|
|
/* If there is no deltaenv to apply, simply return a copy. */
|
|
|
|
if (!deltaenv || !*deltaenv) {
|
|
|
|
for (p = wenv; p && *p; ) {
|
|
|
|
size_t s = wcslen(p) + 1;
|
|
|
|
size += s;
|
|
|
|
p += s;
|
|
|
|
}
|
2014-07-17 15:38:01 +00:00
|
|
|
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
ALLOC_ARRAY(result, size);
|
2019-09-04 11:09:45 +00:00
|
|
|
COPY_ARRAY(result, wenv, size);
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
FreeEnvironmentStringsW(wenv);
|
|
|
|
return result;
|
|
|
|
}
|
2014-07-17 15:38:01 +00:00
|
|
|
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
/*
|
|
|
|
* If there is a deltaenv, let's accumulate all keys into `array`,
|
2019-09-30 17:21:54 +00:00
|
|
|
* sort them using the stable git_stable_qsort() and then copy,
|
|
|
|
* skipping duplicate keys
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
*/
|
|
|
|
for (p = wenv; p && *p; ) {
|
|
|
|
ALLOC_GROW(array, nr + 1, alloc);
|
|
|
|
s = wcslen(p) + 1;
|
|
|
|
array[nr++] = p;
|
|
|
|
p += s;
|
|
|
|
size += s;
|
2014-07-17 15:38:00 +00:00
|
|
|
}
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
|
|
|
|
/* (over-)assess size needed for wchar version of deltaenv */
|
|
|
|
for (delta_size = 0, i = 0; deltaenv[i]; i++)
|
|
|
|
delta_size += strlen(deltaenv[i]) * 2 + 1;
|
|
|
|
ALLOC_ARRAY(wdeltaenv, delta_size);
|
|
|
|
|
|
|
|
/* convert the deltaenv, appending to array */
|
|
|
|
for (i = 0, p = wdeltaenv; deltaenv[i]; i++) {
|
|
|
|
ALLOC_GROW(array, nr + 1, alloc);
|
|
|
|
wlen = xutftowcs(p, deltaenv[i], wdeltaenv + delta_size - p);
|
|
|
|
array[nr++] = p;
|
|
|
|
p += wlen + 1;
|
|
|
|
}
|
2014-07-17 15:38:00 +00:00
|
|
|
|
2019-09-30 17:21:54 +00:00
|
|
|
git_stable_qsort(array, nr, sizeof(*array), wenvcmp);
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
ALLOC_ARRAY(result, size + delta_size);
|
2014-07-17 15:38:00 +00:00
|
|
|
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
for (p = result, i = 0; i < nr; i++) {
|
|
|
|
/* Skip any duplicate keys; last one wins */
|
|
|
|
while (i + 1 < nr && !wenvcmp(array + i, array + i + 1))
|
|
|
|
i++;
|
2014-07-17 15:38:01 +00:00
|
|
|
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
/* Skip "to delete" entry */
|
|
|
|
if (!wcschr(array[i], L'='))
|
|
|
|
continue;
|
2014-07-17 15:38:01 +00:00
|
|
|
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
size = wcslen(array[i]) + 1;
|
2019-09-04 11:09:45 +00:00
|
|
|
COPY_ARRAY(p, array[i], size);
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
p += size;
|
2014-07-17 15:38:00 +00:00
|
|
|
}
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
*p = L'\0';
|
|
|
|
|
|
|
|
free(array);
|
|
|
|
free(wdeltaenv);
|
|
|
|
FreeEnvironmentStringsW(wenv);
|
|
|
|
return result;
|
2007-11-24 21:49:16 +00:00
|
|
|
}
|
|
|
|
|
2018-10-30 18:40:07 +00:00
|
|
|
static void do_unset_environment_variables(void)
|
|
|
|
{
|
|
|
|
static int done;
|
|
|
|
char *p = unset_environment_variables;
|
|
|
|
|
|
|
|
if (done || !p)
|
|
|
|
return;
|
|
|
|
done = 1;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
char *comma = strchr(p, ',');
|
|
|
|
|
|
|
|
if (comma)
|
|
|
|
*comma = '\0';
|
|
|
|
unsetenv(p);
|
|
|
|
if (!comma)
|
|
|
|
break;
|
|
|
|
p = comma + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-04 01:35:13 +00:00
|
|
|
struct pinfo_t {
|
|
|
|
struct pinfo_t *next;
|
|
|
|
pid_t pid;
|
|
|
|
HANDLE proc;
|
2013-04-27 19:18:55 +00:00
|
|
|
};
|
|
|
|
static struct pinfo_t *pinfo = NULL;
|
2010-11-04 01:35:13 +00:00
|
|
|
CRITICAL_SECTION pinfo_cs;
|
|
|
|
|
mingw: special-case arguments to `sh`
The MSYS2 runtime does its best to emulate the command-line wildcard
expansion and de-quoting which would be performed by the calling Unix
shell on Unix systems.
Those Unix shell quoting rules differ from the quoting rules applying to
Windows' cmd and Powershell, making it a little awkward to quote
command-line parameters properly when spawning other processes.
In particular, git.exe passes arguments to subprocesses that are *not*
intended to be interpreted as wildcards, and if they contain
backslashes, those are not to be interpreted as escape characters, e.g.
when passing Windows paths.
Note: this is only a problem when calling MSYS2 executables, not when
calling MINGW executables such as git.exe. However, we do call MSYS2
executables frequently, most notably when setting the use_shell flag in
the child_process structure.
There is no elegant way to determine whether the .exe file to be
executed is an MSYS2 program or a MINGW one. But since the use case of
passing a command line through the shell is so prevalent, we need to
work around this issue at least when executing sh.exe.
Let's introduce an ugly, hard-coded test whether argv[0] is "sh", and
whether it refers to the MSYS2 Bash, to determine whether we need to
quote the arguments differently than usual.
That still does not fix the issue completely, but at least it is
something.
Incidentally, this also fixes the problem where `git clone \\server\repo`
failed due to incorrect handling of the backslashes when handing the path
to the git-upload-pack process.
Further, we need to take care to quote not only whitespace and
backslashes, but also curly brackets. As aliases frequently go through
the MSYS2 Bash, and as aliases frequently get parameters such as
HEAD@{yesterday}, this is really important. As an early version of this
patch broke this, let's make sure that this does not regress by adding a
test case for that.
Helped-by: Kim Gybels <kgybels@infogroep.be>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-01-17 20:14:48 +00:00
|
|
|
/* Used to match and chomp off path components */
|
|
|
|
static inline int match_last_path_component(const char *path, size_t *len,
|
|
|
|
const char *component)
|
|
|
|
{
|
|
|
|
size_t component_len = strlen(component);
|
|
|
|
if (*len < component_len + 1 ||
|
|
|
|
!is_dir_sep(path[*len - component_len - 1]) ||
|
|
|
|
fspathncmp(path + *len - component_len, component, component_len))
|
|
|
|
return 0;
|
|
|
|
*len -= component_len + 1;
|
|
|
|
/* chomp off repeated dir separators */
|
|
|
|
while (*len > 0 && is_dir_sep(path[*len - 1]))
|
|
|
|
(*len)--;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int is_msys2_sh(const char *cmd)
|
|
|
|
{
|
2019-09-19 15:05:21 +00:00
|
|
|
if (!cmd)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!strcmp(cmd, "sh")) {
|
mingw: special-case arguments to `sh`
The MSYS2 runtime does its best to emulate the command-line wildcard
expansion and de-quoting which would be performed by the calling Unix
shell on Unix systems.
Those Unix shell quoting rules differ from the quoting rules applying to
Windows' cmd and Powershell, making it a little awkward to quote
command-line parameters properly when spawning other processes.
In particular, git.exe passes arguments to subprocesses that are *not*
intended to be interpreted as wildcards, and if they contain
backslashes, those are not to be interpreted as escape characters, e.g.
when passing Windows paths.
Note: this is only a problem when calling MSYS2 executables, not when
calling MINGW executables such as git.exe. However, we do call MSYS2
executables frequently, most notably when setting the use_shell flag in
the child_process structure.
There is no elegant way to determine whether the .exe file to be
executed is an MSYS2 program or a MINGW one. But since the use case of
passing a command line through the shell is so prevalent, we need to
work around this issue at least when executing sh.exe.
Let's introduce an ugly, hard-coded test whether argv[0] is "sh", and
whether it refers to the MSYS2 Bash, to determine whether we need to
quote the arguments differently than usual.
That still does not fix the issue completely, but at least it is
something.
Incidentally, this also fixes the problem where `git clone \\server\repo`
failed due to incorrect handling of the backslashes when handing the path
to the git-upload-pack process.
Further, we need to take care to quote not only whitespace and
backslashes, but also curly brackets. As aliases frequently go through
the MSYS2 Bash, and as aliases frequently get parameters such as
HEAD@{yesterday}, this is really important. As an early version of this
patch broke this, let's make sure that this does not regress by adding a
test case for that.
Helped-by: Kim Gybels <kgybels@infogroep.be>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-01-17 20:14:48 +00:00
|
|
|
static int ret = -1;
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
if (ret >= 0)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
p = path_lookup(cmd, 0);
|
|
|
|
if (!p)
|
|
|
|
ret = 0;
|
|
|
|
else {
|
|
|
|
size_t len = strlen(p);
|
|
|
|
|
|
|
|
ret = match_last_path_component(p, &len, "sh.exe") &&
|
|
|
|
match_last_path_component(p, &len, "bin") &&
|
|
|
|
match_last_path_component(p, &len, "usr");
|
|
|
|
free(p);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
2019-09-19 15:05:21 +00:00
|
|
|
|
|
|
|
if (ends_with(cmd, "\\sh.exe")) {
|
|
|
|
static char *sh;
|
|
|
|
|
|
|
|
if (!sh)
|
|
|
|
sh = path_lookup("sh", 0);
|
|
|
|
|
|
|
|
return !fspathcmp(cmd, sh);
|
|
|
|
}
|
|
|
|
|
mingw: special-case arguments to `sh`
The MSYS2 runtime does its best to emulate the command-line wildcard
expansion and de-quoting which would be performed by the calling Unix
shell on Unix systems.
Those Unix shell quoting rules differ from the quoting rules applying to
Windows' cmd and Powershell, making it a little awkward to quote
command-line parameters properly when spawning other processes.
In particular, git.exe passes arguments to subprocesses that are *not*
intended to be interpreted as wildcards, and if they contain
backslashes, those are not to be interpreted as escape characters, e.g.
when passing Windows paths.
Note: this is only a problem when calling MSYS2 executables, not when
calling MINGW executables such as git.exe. However, we do call MSYS2
executables frequently, most notably when setting the use_shell flag in
the child_process structure.
There is no elegant way to determine whether the .exe file to be
executed is an MSYS2 program or a MINGW one. But since the use case of
passing a command line through the shell is so prevalent, we need to
work around this issue at least when executing sh.exe.
Let's introduce an ugly, hard-coded test whether argv[0] is "sh", and
whether it refers to the MSYS2 Bash, to determine whether we need to
quote the arguments differently than usual.
That still does not fix the issue completely, but at least it is
something.
Incidentally, this also fixes the problem where `git clone \\server\repo`
failed due to incorrect handling of the backslashes when handing the path
to the git-upload-pack process.
Further, we need to take care to quote not only whitespace and
backslashes, but also curly brackets. As aliases frequently go through
the MSYS2 Bash, and as aliases frequently get parameters such as
HEAD@{yesterday}, this is really important. As an early version of this
patch broke this, let's make sure that this does not regress by adding a
test case for that.
Helped-by: Kim Gybels <kgybels@infogroep.be>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-01-17 20:14:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-07-17 15:38:01 +00:00
|
|
|
static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaenv,
|
2010-04-11 20:40:12 +00:00
|
|
|
const char *dir,
|
Windows: avoid the "dup dance" when spawning a child process
When stdin, stdout, or stderr must be redirected for a child process that
on Windows is spawned using one of the spawn() functions of Microsoft's
C runtime, then there is no choice other than to
1. make a backup copy of fd 0,1,2 with dup
2. dup2 the redirection source fd into 0,1,2
3. spawn
4. dup2 the backup back into 0,1,2
5. close the backup copy and the redirection source
We used this idiom as well -- but we are not using the spawn() functions
anymore!
Instead, we have our own implementation. We had hardcoded that stdin,
stdout, and stderr of the child process were inherited from the parent's
fds 0, 1, and 2. But we can actually specify any fd.
With this patch, the fds to inherit are passed from start_command()'s
WIN32 section to our spawn implementation. This way, we can avoid the
backup copies of the fds.
The backup copies were a bug waiting to surface: The OS handles underlying
the dup()ed fds were inherited by the child process (but were not
associated with a file descriptor in the child). Consequently, the file or
pipe represented by the OS handle remained open even after the backup copy
was closed in the parent process until the child exited.
Since our implementation of pipe() creates non-inheritable OS handles, we
still dup() file descriptors in start_command() because dup() happens to
create inheritable duplicates. (A nice side effect is that the fd cleanup
in start_command is the same for Windows and Unix and remains unchanged.)
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-01-15 20:12:18 +00:00
|
|
|
int prepend_cmd, int fhin, int fhout, int fherr)
|
2007-11-24 21:49:16 +00:00
|
|
|
{
|
2019-11-22 14:41:05 +00:00
|
|
|
static int restrict_handle_inheritance = -1;
|
mingw: spawned processes need to inherit only standard handles
By default, CreateProcess() does not inherit any open file handles,
unless the bInheritHandles parameter is set to TRUE. Which we do need to
set because we need to pass in stdin/stdout/stderr to talk to the child
processes. Sadly, this means that all file handles (unless marked via
O_NOINHERIT) are inherited.
This lead to problems in VFS for Git, where a long-running read-object
hook is used to hydrate missing objects, and depending on the
circumstances, might only be called *after* Git opened a file handle.
Ideally, we would not open files without O_NOINHERIT unless *really*
necessary (i.e. when we want to pass the opened file handle as standard
handle into a child process), but apparently it is all-too-easy to
introduce incorrect open() calls: this happened, and prevented updating
a file after the read-object hook was started because the hook still
held a handle on said file.
Happily, there is a solution: as described in the "Old New Thing"
https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873 there
is a way, starting with Windows Vista, that lets us define precisely
which handles should be inherited by the child process.
And since we bumped the minimum Windows version for use with Git for
Windows to Vista with v2.10.1 (i.e. a *long* time ago), we can use this
method. So let's do exactly that.
We need to make sure that the list of handles to inherit does not
contain duplicates; Otherwise CreateProcessW() would fail with
ERROR_INVALID_ARGUMENT.
While at it, stop setting errno to ENOENT unless it really is the
correct value.
Also, fall back to not limiting handle inheritance under certain error
conditions (e.g. on Windows 7, which is a lot stricter in what handles
you can specify to limit to).
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-11-22 14:41:04 +00:00
|
|
|
STARTUPINFOEXW si;
|
2007-11-24 21:49:16 +00:00
|
|
|
PROCESS_INFORMATION pi;
|
mingw: spawned processes need to inherit only standard handles
By default, CreateProcess() does not inherit any open file handles,
unless the bInheritHandles parameter is set to TRUE. Which we do need to
set because we need to pass in stdin/stdout/stderr to talk to the child
processes. Sadly, this means that all file handles (unless marked via
O_NOINHERIT) are inherited.
This lead to problems in VFS for Git, where a long-running read-object
hook is used to hydrate missing objects, and depending on the
circumstances, might only be called *after* Git opened a file handle.
Ideally, we would not open files without O_NOINHERIT unless *really*
necessary (i.e. when we want to pass the opened file handle as standard
handle into a child process), but apparently it is all-too-easy to
introduce incorrect open() calls: this happened, and prevented updating
a file after the read-object hook was started because the hook still
held a handle on said file.
Happily, there is a solution: as described in the "Old New Thing"
https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873 there
is a way, starting with Windows Vista, that lets us define precisely
which handles should be inherited by the child process.
And since we bumped the minimum Windows version for use with Git for
Windows to Vista with v2.10.1 (i.e. a *long* time ago), we can use this
method. So let's do exactly that.
We need to make sure that the list of handles to inherit does not
contain duplicates; Otherwise CreateProcessW() would fail with
ERROR_INVALID_ARGUMENT.
While at it, stop setting errno to ENOENT unless it really is the
correct value.
Also, fall back to not limiting handle inheritance under certain error
conditions (e.g. on Windows 7, which is a lot stricter in what handles
you can specify to limit to).
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-11-22 14:41:04 +00:00
|
|
|
LPPROC_THREAD_ATTRIBUTE_LIST attr_list = NULL;
|
|
|
|
HANDLE stdhandles[3];
|
|
|
|
DWORD stdhandles_count = 0;
|
|
|
|
SIZE_T size;
|
2014-07-17 15:37:55 +00:00
|
|
|
struct strbuf args;
|
|
|
|
wchar_t wcmd[MAX_PATH], wdir[MAX_PATH], *wargs, *wenvblk = NULL;
|
|
|
|
unsigned flags = CREATE_UNICODE_ENVIRONMENT;
|
2007-11-24 21:49:16 +00:00
|
|
|
BOOL ret;
|
2018-10-30 18:40:07 +00:00
|
|
|
HANDLE cons;
|
mingw: special-case arguments to `sh`
The MSYS2 runtime does its best to emulate the command-line wildcard
expansion and de-quoting which would be performed by the calling Unix
shell on Unix systems.
Those Unix shell quoting rules differ from the quoting rules applying to
Windows' cmd and Powershell, making it a little awkward to quote
command-line parameters properly when spawning other processes.
In particular, git.exe passes arguments to subprocesses that are *not*
intended to be interpreted as wildcards, and if they contain
backslashes, those are not to be interpreted as escape characters, e.g.
when passing Windows paths.
Note: this is only a problem when calling MSYS2 executables, not when
calling MINGW executables such as git.exe. However, we do call MSYS2
executables frequently, most notably when setting the use_shell flag in
the child_process structure.
There is no elegant way to determine whether the .exe file to be
executed is an MSYS2 program or a MINGW one. But since the use case of
passing a command line through the shell is so prevalent, we need to
work around this issue at least when executing sh.exe.
Let's introduce an ugly, hard-coded test whether argv[0] is "sh", and
whether it refers to the MSYS2 Bash, to determine whether we need to
quote the arguments differently than usual.
That still does not fix the issue completely, but at least it is
something.
Incidentally, this also fixes the problem where `git clone \\server\repo`
failed due to incorrect handling of the backslashes when handing the path
to the git-upload-pack process.
Further, we need to take care to quote not only whitespace and
backslashes, but also curly brackets. As aliases frequently go through
the MSYS2 Bash, and as aliases frequently get parameters such as
HEAD@{yesterday}, this is really important. As an early version of this
patch broke this, let's make sure that this does not regress by adding a
test case for that.
Helped-by: Kim Gybels <kgybels@infogroep.be>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-01-17 20:14:48 +00:00
|
|
|
const char *(*quote_arg)(const char *arg) =
|
2019-09-19 15:05:45 +00:00
|
|
|
is_msys2_sh(cmd ? cmd : *argv) ?
|
|
|
|
quote_arg_msys2 : quote_arg_msvc;
|
2020-04-09 10:21:47 +00:00
|
|
|
const char *strace_env;
|
2018-10-30 18:40:07 +00:00
|
|
|
|
mingw: do set `errno` correctly when trying to restrict handle inheritance
In 9a780a384de (mingw: spawned processes need to inherit only standard
handles, 2019-11-22), we taught the Windows-specific part to restrict
which file handles are passed on to the spawned processes.
Since this logic seemed to be a bit fragile across Windows versions (we
_still_ support Windows Vista in Git for Windows, for example), a
fall-back was added to try spawning the process again, this time without
restricting which file handles are to be inherited by the spawned
process.
In the common case (i.e. when the process could not be spawned for
reasons _other_ than the file handle inheritance), the fall-back attempt
would still fail, of course.
Crucially, one thing we missed in that code path was to set `errno`
appropriately.
This should have been caught by t0061.2 which expected `errno` to be
`ENOENT` after trying to start a process for a non-existing executable,
but `errno` was set to `ENOENT` prior to the `CreateProcessW()` call:
while looking for the config settings for trace2, Git tries to access
`xdg_config` and `user_config` via `access_or_die()`, and as neither of
those config files exists when running the test case (because in Git's
test suite, `HOME` points to the test directory), the `errno` has the
expected value, but for the wrong reasons.
Let's fix that by making sure that `errno` is set correctly. It even
appears that `errno` was set in the _wrong_ case previously:
`CreateProcessW()` returns non-zero upon success, but `errno` was set
only in the non-zero case.
It would be nice if we could somehow fix t0061 to make sure that this
does not regress again. One approach that seemed like it should work,
but did not, was to set `errno` to 0 in the test helper that is used by
t0061.2.
However, when `mingw_spawnvpe()` wants to see whether the file in
question is a script, it calls `parse_interpreter()`, which in turn
tries to `open()` the file. Obviously, this call fails, and sets `errno`
to `ENOENT`, deep inside the call chain started from that test helper.
Instead, we force re-set `errno` at the beginning of the function
`mingw_spawnve_fd()`, which _should_ be safe given that callers of that
function will want to look at `errno` if -1 was returned. And if that
`errno` is 0 ("No error"), regression tests like t0061.2 will kick in.
Reported-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Acked-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-11-30 10:36:39 +00:00
|
|
|
/* Make sure to override previous errors, if any */
|
|
|
|
errno = 0;
|
|
|
|
|
2019-11-22 14:41:05 +00:00
|
|
|
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;
|
|
|
|
|
2018-10-30 18:40:07 +00:00
|
|
|
do_unset_environment_variables();
|
2007-11-24 21:49:16 +00:00
|
|
|
|
|
|
|
/* Determine whether or not we are associated to a console */
|
mingw: use Unicode functions explicitly
Many Win32 API functions actually exist in two variants: one with
the `A` suffix that takes ANSI parameters (`char *` or `const char *`)
and one with the `W` suffix that takes Unicode parameters (`wchar_t *`
or `const wchar_t *`).
The ANSI variant assumes that the strings are encoded according to
whatever is the current locale. This is not what Git wants to use on
Windows: we assume that `char *` variables point to strings encoded in
UTF-8.
There is a pseudo UTF-8 locale on Windows, but it does not work
as one might expect. In addition, if we overrode the user's locale, that
would modify the behavior of programs spawned by Git (such as editors,
difftools, etc), therefore we cannot use that pseudo locale.
Further, it is actually highly encouraged to use the Unicode versions
instead of the ANSI versions, so let's do precisely that.
Note: when calling the Win32 API functions _without_ any suffix, it
depends whether the `UNICODE` constant is defined before the relevant
headers are #include'd. Without that constant, the ANSI variants are
used. Let's be explicit and avoid that ambiguity.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-06-27 09:37:19 +00:00
|
|
|
cons = CreateFileW(L"CONOUT$", GENERIC_WRITE,
|
2007-11-24 21:49:16 +00:00
|
|
|
FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
|
|
|
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if (cons == INVALID_HANDLE_VALUE) {
|
|
|
|
/* There is no console associated with this process.
|
|
|
|
* Since the child is a console process, Windows
|
|
|
|
* would normally create a console window. But
|
|
|
|
* since we'll be redirecting std streams, we do
|
|
|
|
* not need the console.
|
2008-11-02 17:11:13 +00:00
|
|
|
* It is necessary to use DETACHED_PROCESS
|
|
|
|
* instead of CREATE_NO_WINDOW to make ssh
|
|
|
|
* recognize that it has no console.
|
2007-11-24 21:49:16 +00:00
|
|
|
*/
|
2014-07-17 15:37:55 +00:00
|
|
|
flags |= DETACHED_PROCESS;
|
2007-11-24 21:49:16 +00:00
|
|
|
} else {
|
|
|
|
/* There is already a console. If we specified
|
2008-11-02 17:11:13 +00:00
|
|
|
* DETACHED_PROCESS here, too, Windows would
|
2007-11-24 21:49:16 +00:00
|
|
|
* disassociate the child from the console.
|
2008-11-02 17:11:13 +00:00
|
|
|
* The same is true for CREATE_NO_WINDOW.
|
2007-11-24 21:49:16 +00:00
|
|
|
* Go figure!
|
|
|
|
*/
|
|
|
|
CloseHandle(cons);
|
|
|
|
}
|
|
|
|
memset(&si, 0, sizeof(si));
|
mingw: spawned processes need to inherit only standard handles
By default, CreateProcess() does not inherit any open file handles,
unless the bInheritHandles parameter is set to TRUE. Which we do need to
set because we need to pass in stdin/stdout/stderr to talk to the child
processes. Sadly, this means that all file handles (unless marked via
O_NOINHERIT) are inherited.
This lead to problems in VFS for Git, where a long-running read-object
hook is used to hydrate missing objects, and depending on the
circumstances, might only be called *after* Git opened a file handle.
Ideally, we would not open files without O_NOINHERIT unless *really*
necessary (i.e. when we want to pass the opened file handle as standard
handle into a child process), but apparently it is all-too-easy to
introduce incorrect open() calls: this happened, and prevented updating
a file after the read-object hook was started because the hook still
held a handle on said file.
Happily, there is a solution: as described in the "Old New Thing"
https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873 there
is a way, starting with Windows Vista, that lets us define precisely
which handles should be inherited by the child process.
And since we bumped the minimum Windows version for use with Git for
Windows to Vista with v2.10.1 (i.e. a *long* time ago), we can use this
method. So let's do exactly that.
We need to make sure that the list of handles to inherit does not
contain duplicates; Otherwise CreateProcessW() would fail with
ERROR_INVALID_ARGUMENT.
While at it, stop setting errno to ENOENT unless it really is the
correct value.
Also, fall back to not limiting handle inheritance under certain error
conditions (e.g. on Windows 7, which is a lot stricter in what handles
you can specify to limit to).
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-11-22 14:41:04 +00:00
|
|
|
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;
|
2007-11-24 21:49:16 +00:00
|
|
|
|
2019-07-16 14:03:12 +00:00
|
|
|
if (*argv && !strcmp(cmd, *argv))
|
|
|
|
wcmd[0] = L'\0';
|
|
|
|
else if (xutftowcs_path(wcmd, cmd) < 0)
|
2011-01-16 17:27:53 +00:00
|
|
|
return -1;
|
|
|
|
if (dir && xutftowcs_path(wdir, dir) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2007-11-24 21:49:16 +00:00
|
|
|
/* concatenate argv, quoting args as we go */
|
|
|
|
strbuf_init(&args, 0);
|
|
|
|
if (prepend_cmd) {
|
|
|
|
char *quoted = (char *)quote_arg(cmd);
|
|
|
|
strbuf_addstr(&args, quoted);
|
|
|
|
if (quoted != cmd)
|
|
|
|
free(quoted);
|
|
|
|
}
|
|
|
|
for (; *argv; argv++) {
|
|
|
|
char *quoted = (char *)quote_arg(*argv);
|
|
|
|
if (*args.buf)
|
|
|
|
strbuf_addch(&args, ' ');
|
|
|
|
strbuf_addstr(&args, quoted);
|
|
|
|
if (quoted != *argv)
|
|
|
|
free(quoted);
|
|
|
|
}
|
|
|
|
|
2020-04-09 10:21:47 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-29 10:02:59 +00:00
|
|
|
ALLOC_ARRAY(wargs, st_add(st_mult(2, args.len), 1));
|
2011-01-16 17:27:53 +00:00
|
|
|
xutftowcs(wargs, args.buf, 2 * args.len + 1);
|
|
|
|
strbuf_release(&args);
|
|
|
|
|
2014-07-17 15:38:01 +00:00
|
|
|
wenvblk = make_environment_block(deltaenv);
|
2007-11-24 21:49:16 +00:00
|
|
|
|
|
|
|
memset(&pi, 0, sizeof(pi));
|
mingw: spawned processes need to inherit only standard handles
By default, CreateProcess() does not inherit any open file handles,
unless the bInheritHandles parameter is set to TRUE. Which we do need to
set because we need to pass in stdin/stdout/stderr to talk to the child
processes. Sadly, this means that all file handles (unless marked via
O_NOINHERIT) are inherited.
This lead to problems in VFS for Git, where a long-running read-object
hook is used to hydrate missing objects, and depending on the
circumstances, might only be called *after* Git opened a file handle.
Ideally, we would not open files without O_NOINHERIT unless *really*
necessary (i.e. when we want to pass the opened file handle as standard
handle into a child process), but apparently it is all-too-easy to
introduce incorrect open() calls: this happened, and prevented updating
a file after the read-object hook was started because the hook still
held a handle on said file.
Happily, there is a solution: as described in the "Old New Thing"
https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873 there
is a way, starting with Windows Vista, that lets us define precisely
which handles should be inherited by the child process.
And since we bumped the minimum Windows version for use with Git for
Windows to Vista with v2.10.1 (i.e. a *long* time ago), we can use this
method. So let's do exactly that.
We need to make sure that the list of handles to inherit does not
contain duplicates; Otherwise CreateProcessW() would fail with
ERROR_INVALID_ARGUMENT.
While at it, stop setting errno to ENOENT unless it really is the
correct value.
Also, fall back to not limiting handle inheritance under certain error
conditions (e.g. on Windows 7, which is a lot stricter in what handles
you can specify to limit to).
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-11-22 14:41:04 +00:00
|
|
|
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);
|
mingw: do set `errno` correctly when trying to restrict handle inheritance
In 9a780a384de (mingw: spawned processes need to inherit only standard
handles, 2019-11-22), we taught the Windows-specific part to restrict
which file handles are passed on to the spawned processes.
Since this logic seemed to be a bit fragile across Windows versions (we
_still_ support Windows Vista in Git for Windows, for example), a
fall-back was added to try spawning the process again, this time without
restricting which file handles are to be inherited by the spawned
process.
In the common case (i.e. when the process could not be spawned for
reasons _other_ than the file handle inheritance), the fall-back attempt
would still fail, of course.
Crucially, one thing we missed in that code path was to set `errno`
appropriately.
This should have been caught by t0061.2 which expected `errno` to be
`ENOENT` after trying to start a process for a non-existing executable,
but `errno` was set to `ENOENT` prior to the `CreateProcessW()` call:
while looking for the config settings for trace2, Git tries to access
`xdg_config` and `user_config` via `access_or_die()`, and as neither of
those config files exists when running the test case (because in Git's
test suite, `HOME` points to the test directory), the `errno` has the
expected value, but for the wrong reasons.
Let's fix that by making sure that `errno` is set correctly. It even
appears that `errno` was set in the _wrong_ case previously:
`CreateProcessW()` returns non-zero upon success, but `errno` was set
only in the non-zero case.
It would be nice if we could somehow fix t0061 to make sure that this
does not regress again. One approach that seemed like it should work,
but did not, was to set `errno` to 0 in the test helper that is used by
t0061.2.
However, when `mingw_spawnvpe()` wants to see whether the file in
question is a script, it calls `parse_interpreter()`, which in turn
tries to `open()` the file. Obviously, this call fails, and sets `errno`
to `ENOENT`, deep inside the call chain started from that test helper.
Instead, we force re-set `errno` at the beginning of the function
`mingw_spawnve_fd()`, which _should_ be safe given that callers of that
function will want to look at `errno` if -1 was returned. And if that
`errno` is 0 ("No error"), regression tests like t0061.2 will kick in.
Reported-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Acked-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-11-30 10:36:39 +00:00
|
|
|
if (!ret)
|
mingw: spawned processes need to inherit only standard handles
By default, CreateProcess() does not inherit any open file handles,
unless the bInheritHandles parameter is set to TRUE. Which we do need to
set because we need to pass in stdin/stdout/stderr to talk to the child
processes. Sadly, this means that all file handles (unless marked via
O_NOINHERIT) are inherited.
This lead to problems in VFS for Git, where a long-running read-object
hook is used to hydrate missing objects, and depending on the
circumstances, might only be called *after* Git opened a file handle.
Ideally, we would not open files without O_NOINHERIT unless *really*
necessary (i.e. when we want to pass the opened file handle as standard
handle into a child process), but apparently it is all-too-easy to
introduce incorrect open() calls: this happened, and prevented updating
a file after the read-object hook was started because the hook still
held a handle on said file.
Happily, there is a solution: as described in the "Old New Thing"
https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873 there
is a way, starting with Windows Vista, that lets us define precisely
which handles should be inherited by the child process.
And since we bumped the minimum Windows version for use with Git for
Windows to Vista with v2.10.1 (i.e. a *long* time ago), we can use this
method. So let's do exactly that.
We need to make sure that the list of handles to inherit does not
contain duplicates; Otherwise CreateProcessW() would fail with
ERROR_INVALID_ARGUMENT.
While at it, stop setting errno to ENOENT unless it really is the
correct value.
Also, fall back to not limiting handle inheritance under certain error
conditions (e.g. on Windows 7, which is a lot stricter in what handles
you can specify to limit to).
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-11-22 14:41:04 +00:00
|
|
|
errno = err_win_to_posix(GetLastError());
|
mingw: do set `errno` correctly when trying to restrict handle inheritance
In 9a780a384de (mingw: spawned processes need to inherit only standard
handles, 2019-11-22), we taught the Windows-specific part to restrict
which file handles are passed on to the spawned processes.
Since this logic seemed to be a bit fragile across Windows versions (we
_still_ support Windows Vista in Git for Windows, for example), a
fall-back was added to try spawning the process again, this time without
restricting which file handles are to be inherited by the spawned
process.
In the common case (i.e. when the process could not be spawned for
reasons _other_ than the file handle inheritance), the fall-back attempt
would still fail, of course.
Crucially, one thing we missed in that code path was to set `errno`
appropriately.
This should have been caught by t0061.2 which expected `errno` to be
`ENOENT` after trying to start a process for a non-existing executable,
but `errno` was set to `ENOENT` prior to the `CreateProcessW()` call:
while looking for the config settings for trace2, Git tries to access
`xdg_config` and `user_config` via `access_or_die()`, and as neither of
those config files exists when running the test case (because in Git's
test suite, `HOME` points to the test directory), the `errno` has the
expected value, but for the wrong reasons.
Let's fix that by making sure that `errno` is set correctly. It even
appears that `errno` was set in the _wrong_ case previously:
`CreateProcessW()` returns non-zero upon success, but `errno` was set
only in the non-zero case.
It would be nice if we could somehow fix t0061 to make sure that this
does not regress again. One approach that seemed like it should work,
but did not, was to set `errno` to 0 in the test helper that is used by
t0061.2.
However, when `mingw_spawnvpe()` wants to see whether the file in
question is a script, it calls `parse_interpreter()`, which in turn
tries to `open()` the file. Obviously, this call fails, and sets `errno`
to `ENOENT`, deep inside the call chain started from that test helper.
Instead, we force re-set `errno` at the beginning of the function
`mingw_spawnve_fd()`, which _should_ be safe given that callers of that
function will want to look at `errno` if -1 was returned. And if that
`errno` is 0 ("No error"), regression tests like t0061.2 will kick in.
Reported-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Acked-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-11-30 10:36:39 +00:00
|
|
|
if (ret && buf.len) {
|
mingw: spawned processes need to inherit only standard handles
By default, CreateProcess() does not inherit any open file handles,
unless the bInheritHandles parameter is set to TRUE. Which we do need to
set because we need to pass in stdin/stdout/stderr to talk to the child
processes. Sadly, this means that all file handles (unless marked via
O_NOINHERIT) are inherited.
This lead to problems in VFS for Git, where a long-running read-object
hook is used to hydrate missing objects, and depending on the
circumstances, might only be called *after* Git opened a file handle.
Ideally, we would not open files without O_NOINHERIT unless *really*
necessary (i.e. when we want to pass the opened file handle as standard
handle into a child process), but apparently it is all-too-easy to
introduce incorrect open() calls: this happened, and prevented updating
a file after the read-object hook was started because the hook still
held a handle on said file.
Happily, there is a solution: as described in the "Old New Thing"
https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873 there
is a way, starting with Windows Vista, that lets us define precisely
which handles should be inherited by the child process.
And since we bumped the minimum Windows version for use with Git for
Windows to Vista with v2.10.1 (i.e. a *long* time ago), we can use this
method. So let's do exactly that.
We need to make sure that the list of handles to inherit does not
contain duplicates; Otherwise CreateProcessW() would fail with
ERROR_INVALID_ARGUMENT.
While at it, stop setting errno to ENOENT unless it really is the
correct value.
Also, fall back to not limiting handle inheritance under certain error
conditions (e.g. on Windows 7, which is a lot stricter in what handles
you can specify to limit to).
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-11-22 14:41:04 +00:00
|
|
|
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);
|
2007-11-24 21:49:16 +00:00
|
|
|
|
2014-07-17 15:37:55 +00:00
|
|
|
free(wenvblk);
|
2011-01-16 17:27:53 +00:00
|
|
|
free(wargs);
|
2007-11-24 21:49:16 +00:00
|
|
|
|
mingw: spawned processes need to inherit only standard handles
By default, CreateProcess() does not inherit any open file handles,
unless the bInheritHandles parameter is set to TRUE. Which we do need to
set because we need to pass in stdin/stdout/stderr to talk to the child
processes. Sadly, this means that all file handles (unless marked via
O_NOINHERIT) are inherited.
This lead to problems in VFS for Git, where a long-running read-object
hook is used to hydrate missing objects, and depending on the
circumstances, might only be called *after* Git opened a file handle.
Ideally, we would not open files without O_NOINHERIT unless *really*
necessary (i.e. when we want to pass the opened file handle as standard
handle into a child process), but apparently it is all-too-easy to
introduce incorrect open() calls: this happened, and prevented updating
a file after the read-object hook was started because the hook still
held a handle on said file.
Happily, there is a solution: as described in the "Old New Thing"
https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873 there
is a way, starting with Windows Vista, that lets us define precisely
which handles should be inherited by the child process.
And since we bumped the minimum Windows version for use with Git for
Windows to Vista with v2.10.1 (i.e. a *long* time ago), we can use this
method. So let's do exactly that.
We need to make sure that the list of handles to inherit does not
contain duplicates; Otherwise CreateProcessW() would fail with
ERROR_INVALID_ARGUMENT.
While at it, stop setting errno to ENOENT unless it really is the
correct value.
Also, fall back to not limiting handle inheritance under certain error
conditions (e.g. on Windows 7, which is a lot stricter in what handles
you can specify to limit to).
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-11-22 14:41:04 +00:00
|
|
|
if (!ret)
|
2007-11-24 21:49:16 +00:00
|
|
|
return -1;
|
mingw: spawned processes need to inherit only standard handles
By default, CreateProcess() does not inherit any open file handles,
unless the bInheritHandles parameter is set to TRUE. Which we do need to
set because we need to pass in stdin/stdout/stderr to talk to the child
processes. Sadly, this means that all file handles (unless marked via
O_NOINHERIT) are inherited.
This lead to problems in VFS for Git, where a long-running read-object
hook is used to hydrate missing objects, and depending on the
circumstances, might only be called *after* Git opened a file handle.
Ideally, we would not open files without O_NOINHERIT unless *really*
necessary (i.e. when we want to pass the opened file handle as standard
handle into a child process), but apparently it is all-too-easy to
introduce incorrect open() calls: this happened, and prevented updating
a file after the read-object hook was started because the hook still
held a handle on said file.
Happily, there is a solution: as described in the "Old New Thing"
https://blogs.msdn.microsoft.com/oldnewthing/20111216-00/?p=8873 there
is a way, starting with Windows Vista, that lets us define precisely
which handles should be inherited by the child process.
And since we bumped the minimum Windows version for use with Git for
Windows to Vista with v2.10.1 (i.e. a *long* time ago), we can use this
method. So let's do exactly that.
We need to make sure that the list of handles to inherit does not
contain duplicates; Otherwise CreateProcessW() would fail with
ERROR_INVALID_ARGUMENT.
While at it, stop setting errno to ENOENT unless it really is the
correct value.
Also, fall back to not limiting handle inheritance under certain error
conditions (e.g. on Windows 7, which is a lot stricter in what handles
you can specify to limit to).
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-11-22 14:41:04 +00:00
|
|
|
|
2007-11-24 21:49:16 +00:00
|
|
|
CloseHandle(pi.hThread);
|
2010-11-04 01:35:13 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The process ID is the human-readable identifier of the process
|
|
|
|
* that we want to present in log and error messages. The handle
|
|
|
|
* is not useful for this purpose. But we cannot close it, either,
|
|
|
|
* because it is not possible to turn a process ID into a process
|
|
|
|
* handle after the process terminated.
|
|
|
|
* Keep the handle in a list for waitpid.
|
|
|
|
*/
|
|
|
|
EnterCriticalSection(&pinfo_cs);
|
|
|
|
{
|
|
|
|
struct pinfo_t *info = xmalloc(sizeof(struct pinfo_t));
|
|
|
|
info->pid = pi.dwProcessId;
|
|
|
|
info->proc = pi.hProcess;
|
|
|
|
info->next = pinfo;
|
|
|
|
pinfo = info;
|
|
|
|
}
|
|
|
|
LeaveCriticalSection(&pinfo_cs);
|
|
|
|
|
|
|
|
return (pid_t)pi.dwProcessId;
|
2007-11-24 21:49:16 +00:00
|
|
|
}
|
|
|
|
|
2011-11-25 20:33:17 +00:00
|
|
|
static pid_t mingw_spawnv(const char *cmd, const char **argv, int prepend_cmd)
|
Windows: avoid the "dup dance" when spawning a child process
When stdin, stdout, or stderr must be redirected for a child process that
on Windows is spawned using one of the spawn() functions of Microsoft's
C runtime, then there is no choice other than to
1. make a backup copy of fd 0,1,2 with dup
2. dup2 the redirection source fd into 0,1,2
3. spawn
4. dup2 the backup back into 0,1,2
5. close the backup copy and the redirection source
We used this idiom as well -- but we are not using the spawn() functions
anymore!
Instead, we have our own implementation. We had hardcoded that stdin,
stdout, and stderr of the child process were inherited from the parent's
fds 0, 1, and 2. But we can actually specify any fd.
With this patch, the fds to inherit are passed from start_command()'s
WIN32 section to our spawn implementation. This way, we can avoid the
backup copies of the fds.
The backup copies were a bug waiting to surface: The OS handles underlying
the dup()ed fds were inherited by the child process (but were not
associated with a file descriptor in the child). Consequently, the file or
pipe represented by the OS handle remained open even after the backup copy
was closed in the parent process until the child exited.
Since our implementation of pipe() creates non-inheritable OS handles, we
still dup() file descriptors in start_command() because dup() happens to
create inheritable duplicates. (A nice side effect is that the fd cleanup
in start_command is the same for Windows and Unix and remains unchanged.)
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-01-15 20:12:18 +00:00
|
|
|
{
|
2014-07-17 15:38:01 +00:00
|
|
|
return mingw_spawnve_fd(cmd, argv, NULL, NULL, prepend_cmd, 0, 1, 2);
|
Windows: avoid the "dup dance" when spawning a child process
When stdin, stdout, or stderr must be redirected for a child process that
on Windows is spawned using one of the spawn() functions of Microsoft's
C runtime, then there is no choice other than to
1. make a backup copy of fd 0,1,2 with dup
2. dup2 the redirection source fd into 0,1,2
3. spawn
4. dup2 the backup back into 0,1,2
5. close the backup copy and the redirection source
We used this idiom as well -- but we are not using the spawn() functions
anymore!
Instead, we have our own implementation. We had hardcoded that stdin,
stdout, and stderr of the child process were inherited from the parent's
fds 0, 1, and 2. But we can actually specify any fd.
With this patch, the fds to inherit are passed from start_command()'s
WIN32 section to our spawn implementation. This way, we can avoid the
backup copies of the fds.
The backup copies were a bug waiting to surface: The OS handles underlying
the dup()ed fds were inherited by the child process (but were not
associated with a file descriptor in the child). Consequently, the file or
pipe represented by the OS handle remained open even after the backup copy
was closed in the parent process until the child exited.
Since our implementation of pipe() creates non-inheritable OS handles, we
still dup() file descriptors in start_command() because dup() happens to
create inheritable duplicates. (A nice side effect is that the fd cleanup
in start_command is the same for Windows and Unix and remains unchanged.)
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-01-15 20:12:18 +00:00
|
|
|
}
|
|
|
|
|
2014-07-17 15:38:01 +00:00
|
|
|
pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **deltaenv,
|
2010-04-11 20:40:12 +00:00
|
|
|
const char *dir,
|
Windows: avoid the "dup dance" when spawning a child process
When stdin, stdout, or stderr must be redirected for a child process that
on Windows is spawned using one of the spawn() functions of Microsoft's
C runtime, then there is no choice other than to
1. make a backup copy of fd 0,1,2 with dup
2. dup2 the redirection source fd into 0,1,2
3. spawn
4. dup2 the backup back into 0,1,2
5. close the backup copy and the redirection source
We used this idiom as well -- but we are not using the spawn() functions
anymore!
Instead, we have our own implementation. We had hardcoded that stdin,
stdout, and stderr of the child process were inherited from the parent's
fds 0, 1, and 2. But we can actually specify any fd.
With this patch, the fds to inherit are passed from start_command()'s
WIN32 section to our spawn implementation. This way, we can avoid the
backup copies of the fds.
The backup copies were a bug waiting to surface: The OS handles underlying
the dup()ed fds were inherited by the child process (but were not
associated with a file descriptor in the child). Consequently, the file or
pipe represented by the OS handle remained open even after the backup copy
was closed in the parent process until the child exited.
Since our implementation of pipe() creates non-inheritable OS handles, we
still dup() file descriptors in start_command() because dup() happens to
create inheritable duplicates. (A nice side effect is that the fd cleanup
in start_command is the same for Windows and Unix and remains unchanged.)
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-01-15 20:12:18 +00:00
|
|
|
int fhin, int fhout, int fherr)
|
2007-11-24 21:49:16 +00:00
|
|
|
{
|
|
|
|
pid_t pid;
|
2017-05-20 19:35:37 +00:00
|
|
|
char *prog = path_lookup(cmd, 0);
|
2007-11-24 21:49:16 +00:00
|
|
|
|
|
|
|
if (!prog) {
|
|
|
|
errno = ENOENT;
|
|
|
|
pid = -1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const char *interpr = parse_interpreter(prog);
|
|
|
|
|
|
|
|
if (interpr) {
|
|
|
|
const char *argv0 = argv[0];
|
2017-05-20 19:35:37 +00:00
|
|
|
char *iprog = path_lookup(interpr, 1);
|
2007-11-24 21:49:16 +00:00
|
|
|
argv[0] = prog;
|
|
|
|
if (!iprog) {
|
|
|
|
errno = ENOENT;
|
|
|
|
pid = -1;
|
|
|
|
}
|
|
|
|
else {
|
2014-07-17 15:38:01 +00:00
|
|
|
pid = mingw_spawnve_fd(iprog, argv, deltaenv, dir, 1,
|
Windows: avoid the "dup dance" when spawning a child process
When stdin, stdout, or stderr must be redirected for a child process that
on Windows is spawned using one of the spawn() functions of Microsoft's
C runtime, then there is no choice other than to
1. make a backup copy of fd 0,1,2 with dup
2. dup2 the redirection source fd into 0,1,2
3. spawn
4. dup2 the backup back into 0,1,2
5. close the backup copy and the redirection source
We used this idiom as well -- but we are not using the spawn() functions
anymore!
Instead, we have our own implementation. We had hardcoded that stdin,
stdout, and stderr of the child process were inherited from the parent's
fds 0, 1, and 2. But we can actually specify any fd.
With this patch, the fds to inherit are passed from start_command()'s
WIN32 section to our spawn implementation. This way, we can avoid the
backup copies of the fds.
The backup copies were a bug waiting to surface: The OS handles underlying
the dup()ed fds were inherited by the child process (but were not
associated with a file descriptor in the child). Consequently, the file or
pipe represented by the OS handle remained open even after the backup copy
was closed in the parent process until the child exited.
Since our implementation of pipe() creates non-inheritable OS handles, we
still dup() file descriptors in start_command() because dup() happens to
create inheritable duplicates. (A nice side effect is that the fd cleanup
in start_command is the same for Windows and Unix and remains unchanged.)
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-01-15 20:12:18 +00:00
|
|
|
fhin, fhout, fherr);
|
2007-11-24 21:49:16 +00:00
|
|
|
free(iprog);
|
|
|
|
}
|
|
|
|
argv[0] = argv0;
|
|
|
|
}
|
|
|
|
else
|
2014-07-17 15:38:01 +00:00
|
|
|
pid = mingw_spawnve_fd(prog, argv, deltaenv, dir, 0,
|
Windows: avoid the "dup dance" when spawning a child process
When stdin, stdout, or stderr must be redirected for a child process that
on Windows is spawned using one of the spawn() functions of Microsoft's
C runtime, then there is no choice other than to
1. make a backup copy of fd 0,1,2 with dup
2. dup2 the redirection source fd into 0,1,2
3. spawn
4. dup2 the backup back into 0,1,2
5. close the backup copy and the redirection source
We used this idiom as well -- but we are not using the spawn() functions
anymore!
Instead, we have our own implementation. We had hardcoded that stdin,
stdout, and stderr of the child process were inherited from the parent's
fds 0, 1, and 2. But we can actually specify any fd.
With this patch, the fds to inherit are passed from start_command()'s
WIN32 section to our spawn implementation. This way, we can avoid the
backup copies of the fds.
The backup copies were a bug waiting to surface: The OS handles underlying
the dup()ed fds were inherited by the child process (but were not
associated with a file descriptor in the child). Consequently, the file or
pipe represented by the OS handle remained open even after the backup copy
was closed in the parent process until the child exited.
Since our implementation of pipe() creates non-inheritable OS handles, we
still dup() file descriptors in start_command() because dup() happens to
create inheritable duplicates. (A nice side effect is that the fd cleanup
in start_command is the same for Windows and Unix and remains unchanged.)
Signed-off-by: Johannes Sixt <j6t@kdbg.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2010-01-15 20:12:18 +00:00
|
|
|
fhin, fhout, fherr);
|
2007-11-24 21:49:16 +00:00
|
|
|
free(prog);
|
|
|
|
}
|
|
|
|
return pid;
|
|
|
|
}
|
|
|
|
|
2011-11-25 20:33:17 +00:00
|
|
|
static int try_shell_exec(const char *cmd, char *const *argv)
|
2007-12-04 11:38:32 +00:00
|
|
|
{
|
|
|
|
const char *interpr = parse_interpreter(cmd);
|
|
|
|
char *prog;
|
|
|
|
int pid = 0;
|
|
|
|
|
|
|
|
if (!interpr)
|
|
|
|
return 0;
|
2017-05-20 19:35:37 +00:00
|
|
|
prog = path_lookup(interpr, 1);
|
2007-12-04 11:38:32 +00:00
|
|
|
if (prog) {
|
2019-02-22 22:25:01 +00:00
|
|
|
int exec_id;
|
2007-12-04 11:38:32 +00:00
|
|
|
int argc = 0;
|
2019-06-19 21:06:02 +00:00
|
|
|
#ifndef _MSC_VER
|
|
|
|
const
|
|
|
|
#endif
|
|
|
|
char **argv2;
|
2007-12-04 11:38:32 +00:00
|
|
|
while (argv[argc]) argc++;
|
2016-02-22 22:44:25 +00:00
|
|
|
ALLOC_ARRAY(argv2, argc + 1);
|
2007-11-24 21:49:16 +00:00
|
|
|
argv2[0] = (char *)cmd; /* full path to the script file */
|
2019-11-12 21:41:14 +00:00
|
|
|
COPY_ARRAY(&argv2[1], &argv[1], argc);
|
2019-02-22 22:25:01 +00:00
|
|
|
exec_id = trace2_exec(prog, argv2);
|
2011-11-25 20:33:17 +00:00
|
|
|
pid = mingw_spawnv(prog, argv2, 1);
|
2007-12-04 11:38:32 +00:00
|
|
|
if (pid >= 0) {
|
|
|
|
int status;
|
|
|
|
if (waitpid(pid, &status, 0) < 0)
|
|
|
|
status = 255;
|
2019-02-22 22:25:01 +00:00
|
|
|
trace2_exec_result(exec_id, status);
|
2007-12-04 11:38:32 +00:00
|
|
|
exit(status);
|
|
|
|
}
|
2019-02-22 22:25:01 +00:00
|
|
|
trace2_exec_result(exec_id, -1);
|
2007-12-04 11:38:32 +00:00
|
|
|
pid = 1; /* indicate that we tried but failed */
|
|
|
|
free(prog);
|
|
|
|
free(argv2);
|
|
|
|
}
|
|
|
|
return pid;
|
|
|
|
}
|
|
|
|
|
2011-11-25 20:33:17 +00:00
|
|
|
int mingw_execv(const char *cmd, char *const *argv)
|
2007-12-04 11:38:32 +00:00
|
|
|
{
|
|
|
|
/* check if git_command is a shell script */
|
2011-11-25 20:33:17 +00:00
|
|
|
if (!try_shell_exec(cmd, argv)) {
|
2007-12-04 11:38:32 +00:00
|
|
|
int pid, status;
|
2019-02-22 22:25:01 +00:00
|
|
|
int exec_id;
|
2007-12-04 11:38:32 +00:00
|
|
|
|
2019-02-22 22:25:01 +00:00
|
|
|
exec_id = trace2_exec(cmd, (const char **)argv);
|
2011-11-25 20:33:17 +00:00
|
|
|
pid = mingw_spawnv(cmd, (const char **)argv, 0);
|
2019-02-22 22:25:01 +00:00
|
|
|
if (pid < 0) {
|
|
|
|
trace2_exec_result(exec_id, -1);
|
2012-05-29 02:21:39 +00:00
|
|
|
return -1;
|
2019-02-22 22:25:01 +00:00
|
|
|
}
|
2007-12-04 11:38:32 +00:00
|
|
|
if (waitpid(pid, &status, 0) < 0)
|
|
|
|
status = 255;
|
2019-02-22 22:25:01 +00:00
|
|
|
trace2_exec_result(exec_id, status);
|
2007-12-04 11:38:32 +00:00
|
|
|
exit(status);
|
|
|
|
}
|
2012-05-29 02:21:39 +00:00
|
|
|
return -1;
|
2007-12-04 11:38:32 +00:00
|
|
|
}
|
|
|
|
|
2012-04-05 17:48:46 +00:00
|
|
|
int mingw_execvp(const char *cmd, char *const *argv)
|
2007-12-04 11:38:32 +00:00
|
|
|
{
|
2017-05-20 19:35:37 +00:00
|
|
|
char *prog = path_lookup(cmd, 0);
|
2007-12-04 11:38:32 +00:00
|
|
|
|
|
|
|
if (prog) {
|
2011-11-25 20:33:17 +00:00
|
|
|
mingw_execv(prog, argv);
|
2007-12-04 11:38:32 +00:00
|
|
|
free(prog);
|
|
|
|
} else
|
|
|
|
errno = ENOENT;
|
|
|
|
|
2012-04-05 17:48:46 +00:00
|
|
|
return -1;
|
2007-12-04 11:38:32 +00:00
|
|
|
}
|
|
|
|
|
2010-11-04 01:35:15 +00:00
|
|
|
int mingw_kill(pid_t pid, int sig)
|
|
|
|
{
|
|
|
|
if (pid > 0 && sig == SIGTERM) {
|
|
|
|
HANDLE h = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
|
|
|
|
|
|
|
|
if (TerminateProcess(h, -1)) {
|
|
|
|
CloseHandle(h);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
errno = err_win_to_posix(GetLastError());
|
|
|
|
CloseHandle(h);
|
|
|
|
return -1;
|
2013-08-08 11:05:38 +00:00
|
|
|
} else if (pid > 0 && sig == 0) {
|
|
|
|
HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
|
|
|
|
if (h) {
|
|
|
|
CloseHandle(h);
|
|
|
|
return 0;
|
|
|
|
}
|
2010-11-04 01:35:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-07-17 15:38:04 +00:00
|
|
|
/*
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
* UTF-8 versions of getenv(), putenv() and unsetenv().
|
|
|
|
* Internally, they use the CRT's stock UNICODE routines
|
|
|
|
* to avoid data loss.
|
2014-07-17 15:38:04 +00:00
|
|
|
*/
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
char *mingw_getenv(const char *name)
|
2014-07-17 15:38:04 +00:00
|
|
|
{
|
mingw: safe-guard a bit more against getenv() problems
Running up to v2.21.0, we fixed two bugs that were made prominent by the
Windows-specific change to retain copies of only the 30 latest getenv()
calls' returned strings, invalidating any copies of previous getenv()
calls' return values.
While this really shines a light onto bugs of the form where we hold
onto getenv()'s return values without copying them, it is also a real
problem for users.
And even if Jeff King's patches merged via 773e408881 (Merge branch
'jk/save-getenv-result', 2019-01-29) provide further work on that front,
we are far from done. Just one example: on Windows, we unset environment
variables when spawning new processes, which potentially invalidates
strings that were previously obtained via getenv(), and therefore we
have to duplicate environment values that are somehow involved in
spawning new processes (e.g. GIT_MAN_VIEWER in show_man_page()).
We do not have a chance to investigate, let address, all of those issues
in time for v2.21.0, so let's at least help Windows users by increasing
the number of getenv() calls' return values that are kept valid. The
number 64 was determined by looking at the average number of getenv()
calls per process in the entire test suite run on Windows (which is
around 40) and then adding a bit for good measure. And it is a power of
two (which would have hit yesterday's theme perfectly).
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-02-15 15:17:45 +00:00
|
|
|
#define GETENV_MAX_RETAIN 64
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
static char *values[GETENV_MAX_RETAIN];
|
|
|
|
static int value_counter;
|
|
|
|
int len_key, len_value;
|
|
|
|
wchar_t *w_key;
|
|
|
|
char *value;
|
|
|
|
wchar_t w_value[32768];
|
2014-07-17 15:38:04 +00:00
|
|
|
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
if (!name || !*name)
|
|
|
|
return NULL;
|
2007-12-07 21:08:59 +00:00
|
|
|
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
len_key = strlen(name) + 1;
|
|
|
|
/* We cannot use xcalloc() here because that uses getenv() itself */
|
|
|
|
w_key = calloc(len_key, sizeof(wchar_t));
|
|
|
|
if (!w_key)
|
|
|
|
die("Out of memory, (tried to allocate %u wchar_t's)", len_key);
|
|
|
|
xutftowcs(w_key, name, len_key);
|
2019-10-04 15:09:30 +00:00
|
|
|
/* GetEnvironmentVariableW() only sets the last error upon failure */
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
len_value = GetEnvironmentVariableW(w_key, w_value, ARRAY_SIZE(w_value));
|
|
|
|
if (!len_value && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
|
|
|
|
free(w_key);
|
|
|
|
return NULL;
|
2007-12-07 21:08:59 +00:00
|
|
|
}
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
free(w_key);
|
|
|
|
|
|
|
|
len_value = len_value * 3 + 1;
|
|
|
|
/* We cannot use xcalloc() here because that uses getenv() itself */
|
|
|
|
value = calloc(len_value, sizeof(char));
|
|
|
|
if (!value)
|
|
|
|
die("Out of memory, (tried to allocate %u bytes)", len_value);
|
|
|
|
xwcstoutf(value, w_value, len_value);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We return `value` which is an allocated value and the caller is NOT
|
|
|
|
* expecting to have to free it, so we keep a round-robin array,
|
|
|
|
* invalidating the buffer after GETENV_MAX_RETAIN getenv() calls.
|
|
|
|
*/
|
|
|
|
free(values[value_counter]);
|
|
|
|
values[value_counter++] = value;
|
|
|
|
if (value_counter >= ARRAY_SIZE(values))
|
|
|
|
value_counter = 0;
|
|
|
|
|
|
|
|
return value;
|
2007-12-07 21:08:59 +00:00
|
|
|
}
|
|
|
|
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
int mingw_putenv(const char *namevalue)
|
2007-12-07 21:08:59 +00:00
|
|
|
{
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
int size;
|
|
|
|
wchar_t *wide, *equal;
|
|
|
|
BOOL result;
|
2007-12-07 21:08:59 +00:00
|
|
|
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
if (!namevalue || !*namevalue)
|
|
|
|
return 0;
|
2007-12-07 21:08:59 +00:00
|
|
|
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
size = strlen(namevalue) * 2 + 1;
|
|
|
|
wide = calloc(size, sizeof(wchar_t));
|
|
|
|
if (!wide)
|
|
|
|
die("Out of memory, (tried to allocate %u wchar_t's)", size);
|
|
|
|
xutftowcs(wide, namevalue, size);
|
|
|
|
equal = wcschr(wide, L'=');
|
|
|
|
if (!equal)
|
|
|
|
result = SetEnvironmentVariableW(wide, NULL);
|
|
|
|
else {
|
|
|
|
*equal = L'\0';
|
|
|
|
result = SetEnvironmentVariableW(wide, equal + 1);
|
2007-12-07 21:08:59 +00:00
|
|
|
}
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
free(wide);
|
2009-09-11 17:40:08 +00:00
|
|
|
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
if (!result)
|
|
|
|
errno = err_win_to_posix(GetLastError());
|
2011-06-06 07:08:13 +00:00
|
|
|
|
mingw: reencode environment variables on the fly (UTF-16 <-> UTF-8)
On Windows, the authoritative environment is encoded in UTF-16. In Git
for Windows, we convert that to UTF-8 (because UTF-16 is *such* a
foreign idea to Git that its source code is unprepared for it).
Previously, out of performance concerns, we converted the entire
environment to UTF-8 in one fell swoop at the beginning, and upon
putenv() and run_command() converted it back.
Having a private copy of the environment comes with its own perils: when
a library used by Git's source code tries to modify the environment, it
does not really work (in Git for Windows' case, libcurl, see
https://github.com/git-for-windows/git/compare/bcad1e6d58^...bcad1e6d58^2
for a glimpse of the issues).
Hence, it makes our environment handling substantially more robust if we
switch to on-the-fly-conversion in `getenv()`/`putenv()` calls. Based
on an initial version in the MSVC context by Jeff Hostetler, this patch
makes it so.
Surprisingly, this has a *positive* effect on speed: at the time when
the current code was written, we tested the performance, and there were
*so many* `getenv()` calls that it seemed better to convert everything
in one go. In the meantime, though, Git has obviously been cleaned up a
bit with regards to `getenv()` calls so that the Git processes spawned
by the test suite use an average of only 40 `getenv()`/`putenv()` calls
over the process lifetime.
Speaking of the entire test suite: the total time spent in the
re-encoding in the current code takes about 32.4 seconds (out of 113
minutes runtime), whereas the code introduced in this patch takes only
about 8.2 seconds in total. Not much, but it proves that we need not be
concerned about the performance impact introduced by this patch.
Helped-by: Jeff Hostetler <jeffhost@microsoft.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2018-10-30 09:22:30 +00:00
|
|
|
return result ? 0 : -1;
|
2011-06-06 07:06:02 +00:00
|
|
|
}
|
|
|
|
|
2009-11-23 22:55:12 +00:00
|
|
|
static void ensure_socket_initialization(void)
|
2007-12-26 12:51:18 +00:00
|
|
|
{
|
|
|
|
WSADATA wsa;
|
2009-11-23 22:55:12 +00:00
|
|
|
static int initialized = 0;
|
|
|
|
|
|
|
|
if (initialized)
|
|
|
|
return;
|
2007-12-26 12:51:18 +00:00
|
|
|
|
|
|
|
if (WSAStartup(MAKEWORD(2,2), &wsa))
|
|
|
|
die("unable to initialize winsock subsystem, error %d",
|
|
|
|
WSAGetLastError());
|
2009-11-23 22:55:50 +00:00
|
|
|
|
2019-02-27 08:43:13 +00:00
|
|
|
atexit((void(*)(void)) WSACleanup);
|
2009-11-23 22:55:12 +00:00
|
|
|
initialized = 1;
|
|
|
|
}
|
|
|
|
|
2011-10-15 14:05:20 +00:00
|
|
|
#undef gethostname
|
|
|
|
int mingw_gethostname(char *name, int namelen)
|
|
|
|
{
|
|
|
|
ensure_socket_initialization();
|
|
|
|
return gethostname(name, namelen);
|
|
|
|
}
|
|
|
|
|
2009-11-23 22:55:12 +00:00
|
|
|
#undef gethostbyname
|
|
|
|
struct hostent *mingw_gethostbyname(const char *host)
|
|
|
|
{
|
|
|
|
ensure_socket_initialization();
|
2007-12-26 12:51:18 +00:00
|
|
|
return gethostbyname(host);
|
|
|
|
}
|
|
|
|
|
2019-02-27 08:43:13 +00:00
|
|
|
#undef getaddrinfo
|
2009-11-23 22:55:50 +00:00
|
|
|
int mingw_getaddrinfo(const char *node, const char *service,
|
|
|
|
const struct addrinfo *hints, struct addrinfo **res)
|
|
|
|
{
|
|
|
|
ensure_socket_initialization();
|
2019-02-27 08:43:13 +00:00
|
|
|
return getaddrinfo(node, service, hints, res);
|
2009-11-23 22:55:50 +00:00
|
|
|
}
|
|
|
|
|
2007-12-26 12:51:18 +00:00
|
|
|
int mingw_socket(int domain, int type, int protocol)
|
|
|
|
{
|
|
|
|
int sockfd;
|
2010-11-04 01:35:09 +00:00
|
|
|
SOCKET s;
|
|
|
|
|
|
|
|
ensure_socket_initialization();
|
|
|
|
s = WSASocket(domain, type, protocol, NULL, 0, 0);
|
2007-12-26 12:51:18 +00:00
|
|
|
if (s == INVALID_SOCKET) {
|
|
|
|
/*
|
|
|
|
* WSAGetLastError() values are regular BSD error codes
|
|
|
|
* biased by WSABASEERR.
|
|
|
|
* However, strerror() does not know about networking
|
|
|
|
* specific errors, which are values beginning at 38 or so.
|
|
|
|
* Therefore, we choose to leave the biased error code
|
|
|
|
* in errno so that _if_ someone looks up the code somewhere,
|
|
|
|
* then it is at least the number that are usually listed.
|
|
|
|
*/
|
|
|
|
errno = WSAGetLastError();
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* convert into a file descriptor */
|
|
|
|
if ((sockfd = _open_osfhandle(s, O_RDWR|O_BINARY)) < 0) {
|
|
|
|
closesocket(s);
|
|
|
|
return error("unable to make a socket file descriptor: %s",
|
|
|
|
strerror(errno));
|
|
|
|
}
|
|
|
|
return sockfd;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef connect
|
|
|
|
int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz)
|
|
|
|
{
|
|
|
|
SOCKET s = (SOCKET)_get_osfhandle(sockfd);
|
|
|
|
return connect(s, sa, sz);
|
|
|
|
}
|
|
|
|
|
2010-11-04 01:35:09 +00:00
|
|
|
#undef bind
|
|
|
|
int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz)
|
|
|
|
{
|
|
|
|
SOCKET s = (SOCKET)_get_osfhandle(sockfd);
|
|
|
|
return bind(s, sa, sz);
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef setsockopt
|
|
|
|
int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen)
|
|
|
|
{
|
|
|
|
SOCKET s = (SOCKET)_get_osfhandle(sockfd);
|
|
|
|
return setsockopt(s, lvl, optname, (const char*)optval, optlen);
|
|
|
|
}
|
|
|
|
|
2011-05-18 20:24:37 +00:00
|
|
|
#undef shutdown
|
|
|
|
int mingw_shutdown(int sockfd, int how)
|
|
|
|
{
|
|
|
|
SOCKET s = (SOCKET)_get_osfhandle(sockfd);
|
|
|
|
return shutdown(s, how);
|
|
|
|
}
|
|
|
|
|
2010-11-04 01:35:09 +00:00
|
|
|
#undef listen
|
|
|
|
int mingw_listen(int sockfd, int backlog)
|
|
|
|
{
|
|
|
|
SOCKET s = (SOCKET)_get_osfhandle(sockfd);
|
|
|
|
return listen(s, backlog);
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef accept
|
|
|
|
int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz)
|
|
|
|
{
|
|
|
|
int sockfd2;
|
|
|
|
|
|
|
|
SOCKET s1 = (SOCKET)_get_osfhandle(sockfd1);
|
|
|
|
SOCKET s2 = accept(s1, sa, sz);
|
|
|
|
|
|
|
|
/* convert into a file descriptor */
|
|
|
|
if ((sockfd2 = _open_osfhandle(s2, O_RDWR|O_BINARY)) < 0) {
|
|
|
|
int err = errno;
|
|
|
|
closesocket(s2);
|
|
|
|
return error("unable to make a socket file descriptor: %s",
|
|
|
|
strerror(err));
|
|
|
|
}
|
|
|
|
return sockfd2;
|
|
|
|
}
|
|
|
|
|
2007-12-07 21:19:40 +00:00
|
|
|
#undef rename
|
|
|
|
int mingw_rename(const char *pold, const char *pnew)
|
|
|
|
{
|
2009-04-03 06:49:59 +00:00
|
|
|
DWORD attrs, gle;
|
|
|
|
int tries = 0;
|
2012-03-15 17:21:28 +00:00
|
|
|
wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
|
|
|
|
if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0)
|
|
|
|
return -1;
|
2008-11-19 16:25:27 +00:00
|
|
|
|
2007-12-07 21:19:40 +00:00
|
|
|
/*
|
|
|
|
* Try native rename() first to get errno right.
|
|
|
|
* It is based on MoveFile(), which cannot overwrite existing files.
|
|
|
|
*/
|
2012-03-15 17:21:28 +00:00
|
|
|
if (!_wrename(wpold, wpnew))
|
2007-12-07 21:19:40 +00:00
|
|
|
return 0;
|
|
|
|
if (errno != EEXIST)
|
|
|
|
return -1;
|
2009-04-03 06:49:59 +00:00
|
|
|
repeat:
|
2012-03-15 17:21:28 +00:00
|
|
|
if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
|
2007-12-07 21:19:40 +00:00
|
|
|
return 0;
|
|
|
|
/* TODO: translate more errors */
|
2009-04-03 06:49:59 +00:00
|
|
|
gle = GetLastError();
|
|
|
|
if (gle == ERROR_ACCESS_DENIED &&
|
2012-03-15 17:21:28 +00:00
|
|
|
(attrs = GetFileAttributesW(wpnew)) != INVALID_FILE_ATTRIBUTES) {
|
2008-11-19 16:25:27 +00:00
|
|
|
if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
|
2016-01-26 14:34:47 +00:00
|
|
|
DWORD attrsold = GetFileAttributesW(wpold);
|
|
|
|
if (attrsold == INVALID_FILE_ATTRIBUTES ||
|
|
|
|
!(attrsold & FILE_ATTRIBUTE_DIRECTORY))
|
|
|
|
errno = EISDIR;
|
|
|
|
else if (!_wrmdir(wpnew))
|
|
|
|
goto repeat;
|
2007-12-07 21:19:40 +00:00
|
|
|
return -1;
|
|
|
|
}
|
2008-11-19 16:25:27 +00:00
|
|
|
if ((attrs & FILE_ATTRIBUTE_READONLY) &&
|
2012-03-15 17:21:28 +00:00
|
|
|
SetFileAttributesW(wpnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
|
|
|
|
if (MoveFileExW(wpold, wpnew, MOVEFILE_REPLACE_EXISTING))
|
2008-11-19 16:25:27 +00:00
|
|
|
return 0;
|
2009-04-03 06:49:59 +00:00
|
|
|
gle = GetLastError();
|
2008-11-19 16:25:27 +00:00
|
|
|
/* revert file attributes on failure */
|
2012-03-15 17:21:28 +00:00
|
|
|
SetFileAttributesW(wpnew, attrs);
|
2008-11-19 16:25:27 +00:00
|
|
|
}
|
2007-12-07 21:19:40 +00:00
|
|
|
}
|
2009-04-03 06:49:59 +00:00
|
|
|
if (tries < ARRAY_SIZE(delay) && gle == ERROR_ACCESS_DENIED) {
|
|
|
|
/*
|
|
|
|
* We assume that some other process had the source or
|
|
|
|
* destination file open at the wrong moment and retry.
|
|
|
|
* In order to give the other process a higher chance to
|
|
|
|
* complete its operation, we give up our time slice now.
|
|
|
|
* If we have to retry again, we do sleep a bit.
|
|
|
|
*/
|
|
|
|
Sleep(delay[tries]);
|
|
|
|
tries++;
|
|
|
|
goto repeat;
|
|
|
|
}
|
2011-02-07 20:51:21 +00:00
|
|
|
if (gle == ERROR_ACCESS_DENIED &&
|
|
|
|
ask_yes_no_if_possible("Rename from '%s' to '%s' failed. "
|
|
|
|
"Should I try again?", pold, pnew))
|
|
|
|
goto repeat;
|
|
|
|
|
2007-12-07 21:19:40 +00:00
|
|
|
errno = EACCES;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-11-07 20:10:31 +00:00
|
|
|
/*
|
|
|
|
* Note that this doesn't return the actual pagesize, but
|
|
|
|
* the allocation granularity. If future Windows specific git code
|
|
|
|
* needs the real getpagesize function, we need to find another solution.
|
|
|
|
*/
|
|
|
|
int mingw_getpagesize(void)
|
|
|
|
{
|
|
|
|
SYSTEM_INFO si;
|
|
|
|
GetSystemInfo(&si);
|
|
|
|
return si.dwAllocationGranularity;
|
|
|
|
}
|
|
|
|
|
2018-10-15 09:47:06 +00:00
|
|
|
/* See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724435.aspx */
|
|
|
|
enum EXTENDED_NAME_FORMAT {
|
|
|
|
NameDisplay = 3,
|
|
|
|
NameUserPrincipal = 8
|
|
|
|
};
|
|
|
|
|
|
|
|
static char *get_extended_user_info(enum EXTENDED_NAME_FORMAT type)
|
|
|
|
{
|
2022-01-08 16:02:30 +00:00
|
|
|
DECLARE_PROC_ADDR(secur32.dll, BOOL, SEC_ENTRY, GetUserNameExW,
|
2018-10-15 09:47:06 +00:00
|
|
|
enum EXTENDED_NAME_FORMAT, LPCWSTR, PULONG);
|
|
|
|
static wchar_t wbuffer[1024];
|
|
|
|
DWORD len;
|
|
|
|
|
|
|
|
if (!INIT_PROC_ADDR(GetUserNameExW))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
len = ARRAY_SIZE(wbuffer);
|
|
|
|
if (GetUserNameExW(type, wbuffer, &len)) {
|
|
|
|
char *converted = xmalloc((len *= 3));
|
|
|
|
if (xwcstoutf(converted, wbuffer, len) >= 0)
|
|
|
|
return converted;
|
|
|
|
free(converted);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2018-10-15 09:47:08 +00:00
|
|
|
char *mingw_query_user_email(void)
|
|
|
|
{
|
|
|
|
return get_extended_user_info(NameUserPrincipal);
|
|
|
|
}
|
|
|
|
|
2007-12-01 20:24:59 +00:00
|
|
|
struct passwd *getpwuid(int uid)
|
|
|
|
{
|
2018-10-15 09:47:05 +00:00
|
|
|
static unsigned initialized;
|
2007-12-01 21:09:17 +00:00
|
|
|
static char user_name[100];
|
2018-10-15 09:47:05 +00:00
|
|
|
static struct passwd *p;
|
2019-06-27 09:37:18 +00:00
|
|
|
wchar_t buf[100];
|
2018-10-15 09:47:05 +00:00
|
|
|
DWORD len;
|
2007-12-01 21:09:17 +00:00
|
|
|
|
2018-10-15 09:47:05 +00:00
|
|
|
if (initialized)
|
|
|
|
return p;
|
2007-12-01 21:09:17 +00:00
|
|
|
|
2019-07-04 22:36:57 +00:00
|
|
|
len = ARRAY_SIZE(buf);
|
2019-06-27 09:37:18 +00:00
|
|
|
if (!GetUserNameW(buf, &len)) {
|
|
|
|
initialized = 1;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (xwcstoutf(user_name, buf, sizeof(user_name)) < 0) {
|
2018-10-15 09:47:05 +00:00
|
|
|
initialized = 1;
|
2007-12-01 21:09:17 +00:00
|
|
|
return NULL;
|
2018-10-15 09:47:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
p = xmalloc(sizeof(*p));
|
|
|
|
p->pw_name = user_name;
|
2018-10-15 09:47:06 +00:00
|
|
|
p->pw_gecos = get_extended_user_info(NameDisplay);
|
|
|
|
if (!p->pw_gecos)
|
|
|
|
p->pw_gecos = "unknown";
|
2018-10-15 09:47:05 +00:00
|
|
|
p->pw_dir = NULL;
|
|
|
|
|
|
|
|
initialized = 1;
|
|
|
|
return p;
|
2007-12-01 20:24:59 +00:00
|
|
|
}
|
|
|
|
|
2007-11-13 09:14:45 +00:00
|
|
|
static HANDLE timer_event;
|
|
|
|
static HANDLE timer_thread;
|
|
|
|
static int timer_interval;
|
|
|
|
static int one_shot;
|
2012-12-04 08:10:38 +00:00
|
|
|
static sig_handler_t timer_fn = SIG_DFL, sigint_fn = SIG_DFL;
|
2007-11-13 09:14:45 +00:00
|
|
|
|
|
|
|
/* The timer works like this:
|
|
|
|
* The thread, ticktack(), is a trivial routine that most of the time
|
|
|
|
* only waits to receive the signal to terminate. The main thread tells
|
|
|
|
* the thread to terminate by setting the timer_event to the signalled
|
|
|
|
* state.
|
|
|
|
* But ticktack() interrupts the wait state after the timer's interval
|
|
|
|
* length to call the signal handler.
|
|
|
|
*/
|
|
|
|
|
2009-09-16 08:20:21 +00:00
|
|
|
static unsigned __stdcall ticktack(void *dummy)
|
2007-11-13 09:14:45 +00:00
|
|
|
{
|
|
|
|
while (WaitForSingleObject(timer_event, timer_interval) == WAIT_TIMEOUT) {
|
2012-12-04 08:10:38 +00:00
|
|
|
mingw_raise(SIGALRM);
|
2007-11-13 09:14:45 +00:00
|
|
|
if (one_shot)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int start_timer_thread(void)
|
|
|
|
{
|
|
|
|
timer_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
|
|
if (timer_event) {
|
|
|
|
timer_thread = (HANDLE) _beginthreadex(NULL, 0, ticktack, NULL, 0, NULL);
|
|
|
|
if (!timer_thread )
|
|
|
|
return errno = ENOMEM,
|
|
|
|
error("cannot start timer thread");
|
|
|
|
} else
|
|
|
|
return errno = ENOMEM,
|
|
|
|
error("cannot allocate resources for timer");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void stop_timer_thread(void)
|
|
|
|
{
|
|
|
|
if (timer_event)
|
|
|
|
SetEvent(timer_event); /* tell thread to terminate */
|
|
|
|
if (timer_thread) {
|
2019-01-29 14:19:31 +00:00
|
|
|
int rc = WaitForSingleObject(timer_thread, 10000);
|
2007-11-13 09:14:45 +00:00
|
|
|
if (rc == WAIT_TIMEOUT)
|
|
|
|
error("timer thread did not terminate timely");
|
|
|
|
else if (rc != WAIT_OBJECT_0)
|
|
|
|
error("waiting for timer thread failed: %lu",
|
|
|
|
GetLastError());
|
|
|
|
CloseHandle(timer_thread);
|
|
|
|
}
|
|
|
|
if (timer_event)
|
|
|
|
CloseHandle(timer_event);
|
|
|
|
timer_event = NULL;
|
|
|
|
timer_thread = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int is_timeval_eq(const struct timeval *i1, const struct timeval *i2)
|
|
|
|
{
|
|
|
|
return i1->tv_sec == i2->tv_sec && i1->tv_usec == i2->tv_usec;
|
|
|
|
}
|
|
|
|
|
2007-12-01 20:24:59 +00:00
|
|
|
int setitimer(int type, struct itimerval *in, struct itimerval *out)
|
|
|
|
{
|
2007-11-13 09:14:45 +00:00
|
|
|
static const struct timeval zero;
|
|
|
|
static int atexit_done;
|
|
|
|
|
|
|
|
if (out != NULL)
|
|
|
|
return errno = EINVAL,
|
|
|
|
error("setitimer param 3 != NULL not implemented");
|
|
|
|
if (!is_timeval_eq(&in->it_interval, &zero) &&
|
|
|
|
!is_timeval_eq(&in->it_interval, &in->it_value))
|
|
|
|
return errno = EINVAL,
|
|
|
|
error("setitimer: it_interval must be zero or eq it_value");
|
|
|
|
|
|
|
|
if (timer_thread)
|
|
|
|
stop_timer_thread();
|
|
|
|
|
|
|
|
if (is_timeval_eq(&in->it_value, &zero) &&
|
|
|
|
is_timeval_eq(&in->it_interval, &zero))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
timer_interval = in->it_value.tv_sec * 1000 + in->it_value.tv_usec / 1000;
|
|
|
|
one_shot = is_timeval_eq(&in->it_interval, &zero);
|
|
|
|
if (!atexit_done) {
|
|
|
|
atexit(stop_timer_thread);
|
|
|
|
atexit_done = 1;
|
|
|
|
}
|
|
|
|
return start_timer_thread();
|
2007-12-01 20:24:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int sigaction(int sig, struct sigaction *in, struct sigaction *out)
|
|
|
|
{
|
2007-11-13 09:14:45 +00:00
|
|
|
if (sig != SIGALRM)
|
|
|
|
return errno = EINVAL,
|
|
|
|
error("sigaction only implemented for SIGALRM");
|
|
|
|
if (out != NULL)
|
|
|
|
return errno = EINVAL,
|
|
|
|
error("sigaction: param 3 != NULL not implemented");
|
|
|
|
|
|
|
|
timer_fn = in->sa_handler;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef signal
|
|
|
|
sig_handler_t mingw_signal(int sig, sig_handler_t handler)
|
|
|
|
{
|
2013-06-10 05:48:17 +00:00
|
|
|
sig_handler_t old;
|
2012-12-04 08:10:38 +00:00
|
|
|
|
|
|
|
switch (sig) {
|
|
|
|
case SIGALRM:
|
2013-06-10 05:48:17 +00:00
|
|
|
old = timer_fn;
|
2012-12-04 08:10:38 +00:00
|
|
|
timer_fn = handler;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SIGINT:
|
2013-06-10 05:48:17 +00:00
|
|
|
old = sigint_fn;
|
2012-12-04 08:10:38 +00:00
|
|
|
sigint_fn = handler;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2007-11-13 09:14:45 +00:00
|
|
|
return signal(sig, handler);
|
2012-12-04 08:10:38 +00:00
|
|
|
}
|
|
|
|
|
2007-11-13 09:14:45 +00:00
|
|
|
return old;
|
2007-12-01 20:24:59 +00:00
|
|
|
}
|
2008-07-13 20:31:20 +00:00
|
|
|
|
2012-12-04 08:10:38 +00:00
|
|
|
#undef raise
|
|
|
|
int mingw_raise(int sig)
|
|
|
|
{
|
|
|
|
switch (sig) {
|
|
|
|
case SIGALRM:
|
|
|
|
if (timer_fn == SIG_DFL) {
|
|
|
|
if (isatty(STDERR_FILENO))
|
|
|
|
fputs("Alarm clock\n", stderr);
|
|
|
|
exit(128 + SIGALRM);
|
|
|
|
} else if (timer_fn != SIG_IGN)
|
|
|
|
timer_fn(SIGALRM);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case SIGINT:
|
|
|
|
if (sigint_fn == SIG_DFL)
|
|
|
|
exit(128 + SIGINT);
|
|
|
|
else if (sigint_fn != SIG_IGN)
|
|
|
|
sigint_fn(SIGINT);
|
|
|
|
return 0;
|
|
|
|
|
2019-06-25 14:49:41 +00:00
|
|
|
#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
|
|
|
|
|
2012-12-04 08:10:38 +00:00
|
|
|
default:
|
|
|
|
return raise(sig);
|
2019-06-25 14:49:41 +00:00
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2012-12-04 08:10:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-24 14:04:39 +00:00
|
|
|
int link(const char *oldpath, const char *newpath)
|
|
|
|
{
|
2012-03-15 17:21:28 +00:00
|
|
|
wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH];
|
|
|
|
if (xutftowcs_path(woldpath, oldpath) < 0 ||
|
|
|
|
xutftowcs_path(wnewpath, newpath) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2018-11-13 14:37:02 +00:00
|
|
|
if (!CreateHardLinkW(wnewpath, woldpath, NULL)) {
|
2009-01-24 14:04:39 +00:00
|
|
|
errno = err_win_to_posix(GetLastError());
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2009-05-23 08:04:50 +00:00
|
|
|
|
2011-12-08 19:39:57 +00:00
|
|
|
pid_t waitpid(pid_t pid, int *status, int options)
|
2010-11-04 01:35:13 +00:00
|
|
|
{
|
|
|
|
HANDLE h = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
|
|
|
|
FALSE, pid);
|
|
|
|
if (!h) {
|
|
|
|
errno = ECHILD;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2010-11-04 01:35:14 +00:00
|
|
|
if (pid > 0 && options & WNOHANG) {
|
|
|
|
if (WAIT_OBJECT_0 != WaitForSingleObject(h, 0)) {
|
|
|
|
CloseHandle(h);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
options &= ~WNOHANG;
|
|
|
|
}
|
|
|
|
|
2010-11-04 01:35:13 +00:00
|
|
|
if (options == 0) {
|
|
|
|
struct pinfo_t **ppinfo;
|
|
|
|
if (WaitForSingleObject(h, INFINITE) != WAIT_OBJECT_0) {
|
|
|
|
CloseHandle(h);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status)
|
|
|
|
GetExitCodeProcess(h, (LPDWORD)status);
|
|
|
|
|
|
|
|
EnterCriticalSection(&pinfo_cs);
|
|
|
|
|
|
|
|
ppinfo = &pinfo;
|
|
|
|
while (*ppinfo) {
|
|
|
|
struct pinfo_t *info = *ppinfo;
|
|
|
|
if (info->pid == pid) {
|
|
|
|
CloseHandle(info->proc);
|
|
|
|
*ppinfo = info->next;
|
|
|
|
free(info);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
ppinfo = &info->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
LeaveCriticalSection(&pinfo_cs);
|
|
|
|
|
|
|
|
CloseHandle(h);
|
|
|
|
return pid;
|
|
|
|
}
|
|
|
|
CloseHandle(h);
|
|
|
|
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
2010-07-13 14:17:43 +00:00
|
|
|
|
2011-11-25 20:05:06 +00:00
|
|
|
int xutftowcsn(wchar_t *wcs, const char *utfs, size_t wcslen, int utflen)
|
|
|
|
{
|
|
|
|
int upos = 0, wpos = 0;
|
|
|
|
const unsigned char *utf = (const unsigned char*) utfs;
|
|
|
|
if (!utf || !wcs || wcslen < 1) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
/* reserve space for \0 */
|
|
|
|
wcslen--;
|
|
|
|
if (utflen < 0)
|
|
|
|
utflen = INT_MAX;
|
|
|
|
|
|
|
|
while (upos < utflen) {
|
|
|
|
int c = utf[upos++] & 0xff;
|
|
|
|
if (utflen == INT_MAX && c == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (wpos >= wcslen) {
|
|
|
|
wcs[wpos] = 0;
|
|
|
|
errno = ERANGE;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c < 0x80) {
|
|
|
|
/* ASCII */
|
|
|
|
wcs[wpos++] = c;
|
|
|
|
} else if (c >= 0xc2 && c < 0xe0 && upos < utflen &&
|
|
|
|
(utf[upos] & 0xc0) == 0x80) {
|
|
|
|
/* 2-byte utf-8 */
|
|
|
|
c = ((c & 0x1f) << 6);
|
|
|
|
c |= (utf[upos++] & 0x3f);
|
|
|
|
wcs[wpos++] = c;
|
|
|
|
} else if (c >= 0xe0 && c < 0xf0 && upos + 1 < utflen &&
|
|
|
|
!(c == 0xe0 && utf[upos] < 0xa0) && /* over-long encoding */
|
|
|
|
(utf[upos] & 0xc0) == 0x80 &&
|
|
|
|
(utf[upos + 1] & 0xc0) == 0x80) {
|
|
|
|
/* 3-byte utf-8 */
|
|
|
|
c = ((c & 0x0f) << 12);
|
|
|
|
c |= ((utf[upos++] & 0x3f) << 6);
|
|
|
|
c |= (utf[upos++] & 0x3f);
|
|
|
|
wcs[wpos++] = c;
|
|
|
|
} else if (c >= 0xf0 && c < 0xf5 && upos + 2 < utflen &&
|
|
|
|
wpos + 1 < wcslen &&
|
|
|
|
!(c == 0xf0 && utf[upos] < 0x90) && /* over-long encoding */
|
|
|
|
!(c == 0xf4 && utf[upos] >= 0x90) && /* > \u10ffff */
|
|
|
|
(utf[upos] & 0xc0) == 0x80 &&
|
|
|
|
(utf[upos + 1] & 0xc0) == 0x80 &&
|
|
|
|
(utf[upos + 2] & 0xc0) == 0x80) {
|
|
|
|
/* 4-byte utf-8: convert to \ud8xx \udcxx surrogate pair */
|
|
|
|
c = ((c & 0x07) << 18);
|
|
|
|
c |= ((utf[upos++] & 0x3f) << 12);
|
|
|
|
c |= ((utf[upos++] & 0x3f) << 6);
|
|
|
|
c |= (utf[upos++] & 0x3f);
|
|
|
|
c -= 0x10000;
|
|
|
|
wcs[wpos++] = 0xd800 | (c >> 10);
|
|
|
|
wcs[wpos++] = 0xdc00 | (c & 0x3ff);
|
|
|
|
} else if (c >= 0xa0) {
|
|
|
|
/* invalid utf-8 byte, printable unicode char: convert 1:1 */
|
|
|
|
wcs[wpos++] = c;
|
|
|
|
} else {
|
|
|
|
/* invalid utf-8 byte, non-printable unicode: convert to hex */
|
|
|
|
static const char *hex = "0123456789abcdef";
|
|
|
|
wcs[wpos++] = hex[c >> 4];
|
|
|
|
if (wpos < wcslen)
|
|
|
|
wcs[wpos++] = hex[c & 0x0f];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wcs[wpos] = 0;
|
|
|
|
return wpos;
|
|
|
|
}
|
|
|
|
|
|
|
|
int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
|
|
|
|
{
|
|
|
|
if (!wcs || !utf || utflen < 1) {
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
utflen = WideCharToMultiByte(CP_UTF8, 0, wcs, -1, utf, utflen, NULL, NULL);
|
|
|
|
if (utflen)
|
|
|
|
return utflen - 1;
|
|
|
|
errno = ERANGE;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-06-18 12:38:36 +00:00
|
|
|
static void setup_windows_environment(void)
|
2016-01-26 14:34:38 +00:00
|
|
|
{
|
2016-01-26 14:34:43 +00:00
|
|
|
char *tmp = getenv("TMPDIR");
|
|
|
|
|
2016-01-26 14:34:38 +00:00
|
|
|
/* on Windows it is TMP and TEMP */
|
2016-01-26 14:34:43 +00:00
|
|
|
if (!tmp) {
|
|
|
|
if (!(tmp = getenv("TMP")))
|
2016-01-26 14:34:38 +00:00
|
|
|
tmp = getenv("TEMP");
|
2016-01-26 14:34:43 +00:00
|
|
|
if (tmp) {
|
2016-01-26 14:34:38 +00:00
|
|
|
setenv("TMPDIR", tmp, 1);
|
2016-01-26 14:34:43 +00:00
|
|
|
tmp = getenv("TMPDIR");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tmp) {
|
|
|
|
/*
|
|
|
|
* Convert all dir separators to forward slashes,
|
|
|
|
* to help shell commands called from the Git
|
|
|
|
* executable (by not mistaking the dir separators
|
|
|
|
* for escape characters).
|
|
|
|
*/
|
2016-04-02 19:03:14 +00:00
|
|
|
convert_slashes(tmp);
|
2016-01-26 14:34:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* simulate TERM to enable auto-color (see color.c) */
|
|
|
|
if (!getenv("TERM"))
|
|
|
|
setenv("TERM", "cygwin", 1);
|
2019-07-04 09:20:33 +00:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|
2016-01-26 14:34:38 +00:00
|
|
|
}
|
|
|
|
|
2022-03-02 10:06:24 +00:00
|
|
|
static PSID get_current_user_sid(void)
|
|
|
|
{
|
|
|
|
HANDLE token;
|
|
|
|
DWORD len = 0;
|
|
|
|
PSID result = NULL;
|
|
|
|
|
|
|
|
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!GetTokenInformation(token, TokenUser, NULL, 0, &len)) {
|
|
|
|
TOKEN_USER *info = xmalloc((size_t)len);
|
|
|
|
if (GetTokenInformation(token, TokenUser, info, len, &len)) {
|
|
|
|
len = GetLengthSid(info->User.Sid);
|
|
|
|
result = xmalloc(len);
|
|
|
|
if (!CopySid(len, result, info->User.Sid)) {
|
|
|
|
error(_("failed to copy SID (%ld)"),
|
|
|
|
GetLastError());
|
|
|
|
FREE_AND_NULL(result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
FREE_AND_NULL(info);
|
|
|
|
}
|
|
|
|
CloseHandle(token);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
int is_path_owned_by_current_sid(const char *path)
|
|
|
|
{
|
|
|
|
WCHAR wpath[MAX_PATH];
|
|
|
|
PSID sid = NULL;
|
|
|
|
PSECURITY_DESCRIPTOR descriptor = NULL;
|
|
|
|
DWORD err;
|
|
|
|
|
|
|
|
static wchar_t home[MAX_PATH];
|
|
|
|
|
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
if (xutftowcs_path(wpath, path) < 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* On Windows, the home directory is owned by the administrator, but for
|
|
|
|
* all practical purposes, it belongs to the user. Do pretend that it is
|
|
|
|
* owned by the user.
|
|
|
|
*/
|
|
|
|
if (!*home) {
|
|
|
|
DWORD size = ARRAY_SIZE(home);
|
|
|
|
DWORD len = GetEnvironmentVariableW(L"HOME", home, size);
|
|
|
|
if (!len || len > size)
|
|
|
|
wcscpy(home, L"::N/A::");
|
|
|
|
}
|
|
|
|
if (!wcsicmp(wpath, home))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
/* Get the owner SID */
|
|
|
|
err = GetNamedSecurityInfoW(wpath, SE_FILE_OBJECT,
|
|
|
|
OWNER_SECURITY_INFORMATION |
|
|
|
|
DACL_SECURITY_INFORMATION,
|
|
|
|
&sid, NULL, NULL, NULL, &descriptor);
|
|
|
|
|
|
|
|
if (err != ERROR_SUCCESS)
|
|
|
|
error(_("failed to get owner for '%s' (%ld)"), path, err);
|
|
|
|
else if (sid && IsValidSid(sid)) {
|
|
|
|
/* Now, verify that the SID matches the current user's */
|
|
|
|
static PSID current_user_sid;
|
|
|
|
|
|
|
|
if (!current_user_sid)
|
|
|
|
current_user_sid = get_current_user_sid();
|
|
|
|
|
|
|
|
if (current_user_sid &&
|
|
|
|
IsValidSid(current_user_sid) &&
|
|
|
|
EqualSid(sid, current_user_sid))
|
|
|
|
result = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We can release the security descriptor struct only now because `sid`
|
|
|
|
* actually points into this struct.
|
|
|
|
*/
|
|
|
|
if (descriptor)
|
|
|
|
LocalFree(descriptor);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-12-21 22:05:01 +00:00
|
|
|
int is_valid_win32_path(const char *path, int allow_literal_nul)
|
mingw: refuse to access paths with trailing spaces or periods
When creating a directory on Windows whose path ends in a space or a
period (or chains thereof), the Win32 API "helpfully" trims those. For
example, `mkdir("abc ");` will return success, but actually create a
directory called `abc` instead.
This stems back to the DOS days, when all file names had exactly 8
characters plus exactly 3 characters for the file extension, and the
only way to have shorter names was by padding with spaces.
Sadly, this "helpful" behavior is a bit inconsistent: after a successful
`mkdir("abc ");`, a `mkdir("abc /def")` will actually _fail_ (because
the directory `abc ` does not actually exist).
Even if it would work, we now have a serious problem because a Git
repository could contain directories `abc` and `abc `, and on Windows,
they would be "merged" unintentionally.
As these paths are illegal on Windows, anyway, let's disallow any
accesses to such paths on that Operating System.
For practical reasons, this behavior is still guarded by the
config setting `core.protectNTFS`: it is possible (and at least two
regression tests make use of it) to create commits without involving the
worktree. In such a scenario, it is of course possible -- even on
Windows -- to create such file names.
Among other consequences, this patch disallows submodules' paths to end
in spaces on Windows (which would formerly have confused Git enough to
try to write into incorrect paths, anyway).
While this patch does not fix a vulnerability on its own, it prevents an
attack vector that was exploited in demonstrations of a number of
recently-fixed security bugs.
The regression test added to `t/t7417-submodule-path-url.sh` reflects
that attack vector.
Note that we have to adjust the test case "prevent git~1 squatting on
Windows" in `t/t7415-submodule-names.sh` because of a very subtle issue.
It tries to clone two submodules whose names differ only in a trailing
period character, and as a consequence their git directories differ in
the same way. Previously, when Git tried to clone the second submodule,
it thought that the git directory already existed (because on Windows,
when you create a directory with the name `b.` it actually creates `b`),
but with this patch, the first submodule's clone will fail because of
the illegal name of the git directory. Therefore, when cloning the
second submodule, Git will take a different code path: a fresh clone
(without an existing git directory). Both code paths fail to clone the
second submodule, both because the the corresponding worktree directory
exists and is not empty, but the error messages are worded differently.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-05 11:27:53 +00:00
|
|
|
{
|
2019-12-21 22:05:01 +00:00
|
|
|
const char *p = path;
|
mingw: refuse to access paths with trailing spaces or periods
When creating a directory on Windows whose path ends in a space or a
period (or chains thereof), the Win32 API "helpfully" trims those. For
example, `mkdir("abc ");` will return success, but actually create a
directory called `abc` instead.
This stems back to the DOS days, when all file names had exactly 8
characters plus exactly 3 characters for the file extension, and the
only way to have shorter names was by padding with spaces.
Sadly, this "helpful" behavior is a bit inconsistent: after a successful
`mkdir("abc ");`, a `mkdir("abc /def")` will actually _fail_ (because
the directory `abc ` does not actually exist).
Even if it would work, we now have a serious problem because a Git
repository could contain directories `abc` and `abc `, and on Windows,
they would be "merged" unintentionally.
As these paths are illegal on Windows, anyway, let's disallow any
accesses to such paths on that Operating System.
For practical reasons, this behavior is still guarded by the
config setting `core.protectNTFS`: it is possible (and at least two
regression tests make use of it) to create commits without involving the
worktree. In such a scenario, it is of course possible -- even on
Windows -- to create such file names.
Among other consequences, this patch disallows submodules' paths to end
in spaces on Windows (which would formerly have confused Git enough to
try to write into incorrect paths, anyway).
While this patch does not fix a vulnerability on its own, it prevents an
attack vector that was exploited in demonstrations of a number of
recently-fixed security bugs.
The regression test added to `t/t7417-submodule-path-url.sh` reflects
that attack vector.
Note that we have to adjust the test case "prevent git~1 squatting on
Windows" in `t/t7415-submodule-names.sh` because of a very subtle issue.
It tries to clone two submodules whose names differ only in a trailing
period character, and as a consequence their git directories differ in
the same way. Previously, when Git tried to clone the second submodule,
it thought that the git directory already existed (because on Windows,
when you create a directory with the name `b.` it actually creates `b`),
but with this patch, the first submodule's clone will fail because of
the illegal name of the git directory. Therefore, when cloning the
second submodule, Git will take a different code path: a fresh clone
(without an existing git directory). Both code paths fail to clone the
second submodule, both because the the corresponding worktree directory
exists and is not empty, but the error messages are worded differently.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-05 11:27:53 +00:00
|
|
|
int preceding_space_or_period = 0, i = 0, periods = 0;
|
|
|
|
|
|
|
|
if (!protect_ntfs)
|
|
|
|
return 1;
|
|
|
|
|
mingw: refuse to access paths with illegal characters
Certain characters are not admissible in file names on Windows, even if
Cygwin/MSYS2 (and therefore, Git for Windows' Bash) pretend that they
are, e.g. `:`, `<`, `>`, etc
Let's disallow those characters explicitly in Windows builds of Git.
Note: just like trailing spaces or periods, it _is_ possible on Windows
to create commits adding files with such illegal characters, as long as
the operation leaves the worktree untouched. To allow for that, we
continue to guard `is_valid_win32_path()` behind the config setting
`core.protectNTFS`, so that users _can_ continue to do that, as long as
they turn the protections off via that config setting.
Among other problems, this prevents Git from trying to write to an "NTFS
Alternate Data Stream" (which refers to metadata stored alongside a
file, under a special name: "<filename>:<stream-name>"). This fix
therefore also prevents an attack vector that was exploited in
demonstrations of a number of recently-fixed security bugs.
Further reading on illegal characters in Win32 filenames:
https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-05 11:44:21 +00:00
|
|
|
skip_dos_drive_prefix((char **)&path);
|
2019-12-21 22:05:01 +00:00
|
|
|
goto segment_start;
|
mingw: refuse to access paths with illegal characters
Certain characters are not admissible in file names on Windows, even if
Cygwin/MSYS2 (and therefore, Git for Windows' Bash) pretend that they
are, e.g. `:`, `<`, `>`, etc
Let's disallow those characters explicitly in Windows builds of Git.
Note: just like trailing spaces or periods, it _is_ possible on Windows
to create commits adding files with such illegal characters, as long as
the operation leaves the worktree untouched. To allow for that, we
continue to guard `is_valid_win32_path()` behind the config setting
`core.protectNTFS`, so that users _can_ continue to do that, as long as
they turn the protections off via that config setting.
Among other problems, this prevents Git from trying to write to an "NTFS
Alternate Data Stream" (which refers to metadata stored alongside a
file, under a special name: "<filename>:<stream-name>"). This fix
therefore also prevents an attack vector that was exploited in
demonstrations of a number of recently-fixed security bugs.
Further reading on illegal characters in Win32 filenames:
https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-05 11:44:21 +00:00
|
|
|
|
mingw: refuse to access paths with trailing spaces or periods
When creating a directory on Windows whose path ends in a space or a
period (or chains thereof), the Win32 API "helpfully" trims those. For
example, `mkdir("abc ");` will return success, but actually create a
directory called `abc` instead.
This stems back to the DOS days, when all file names had exactly 8
characters plus exactly 3 characters for the file extension, and the
only way to have shorter names was by padding with spaces.
Sadly, this "helpful" behavior is a bit inconsistent: after a successful
`mkdir("abc ");`, a `mkdir("abc /def")` will actually _fail_ (because
the directory `abc ` does not actually exist).
Even if it would work, we now have a serious problem because a Git
repository could contain directories `abc` and `abc `, and on Windows,
they would be "merged" unintentionally.
As these paths are illegal on Windows, anyway, let's disallow any
accesses to such paths on that Operating System.
For practical reasons, this behavior is still guarded by the
config setting `core.protectNTFS`: it is possible (and at least two
regression tests make use of it) to create commits without involving the
worktree. In such a scenario, it is of course possible -- even on
Windows -- to create such file names.
Among other consequences, this patch disallows submodules' paths to end
in spaces on Windows (which would formerly have confused Git enough to
try to write into incorrect paths, anyway).
While this patch does not fix a vulnerability on its own, it prevents an
attack vector that was exploited in demonstrations of a number of
recently-fixed security bugs.
The regression test added to `t/t7417-submodule-path-url.sh` reflects
that attack vector.
Note that we have to adjust the test case "prevent git~1 squatting on
Windows" in `t/t7415-submodule-names.sh` because of a very subtle issue.
It tries to clone two submodules whose names differ only in a trailing
period character, and as a consequence their git directories differ in
the same way. Previously, when Git tried to clone the second submodule,
it thought that the git directory already existed (because on Windows,
when you create a directory with the name `b.` it actually creates `b`),
but with this patch, the first submodule's clone will fail because of
the illegal name of the git directory. Therefore, when cloning the
second submodule, Git will take a different code path: a fresh clone
(without an existing git directory). Both code paths fail to clone the
second submodule, both because the the corresponding worktree directory
exists and is not empty, but the error messages are worded differently.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-05 11:27:53 +00:00
|
|
|
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;
|
2019-12-21 22:05:01 +00:00
|
|
|
|
|
|
|
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;
|
mingw: do not treat `COM0` as a reserved file name
In 4dc42c6c186 (mingw: refuse paths containing reserved names,
2019-12-21), we started disallowing file names that are reserved, e.g.
`NUL`, `CONOUT$`, etc.
This included `COM<n>` where `<n>` is a digit. Unfortunately, this
includes `COM0` but only `COM1`, ..., `COM9` are reserved, according to
the official documentation, `COM0` is mentioned in the "NT Namespaces"
section but it is explicitly _omitted_ from the list of reserved names:
https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions
Tests corroborate this: it is totally possible to write a file called
`com0.c` on Windows 10, but not `com1.c`.
So let's tighten the code to disallow only the reserved `COM<n>` file
names, but to allow `COM0` again.
This fixes https://github.com/git-for-windows/git/issues/2470.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-08 18:06:49 +00:00
|
|
|
case 'c': case 'C':
|
|
|
|
/* COM1 ... COM9, CON, CONIN$, CONOUT$ */
|
2019-12-21 22:05:01 +00:00
|
|
|
if ((c = path[++i]) != 'o' && c != 'O')
|
|
|
|
goto not_a_reserved_name;
|
|
|
|
c = path[++i];
|
mingw: do not treat `COM0` as a reserved file name
In 4dc42c6c186 (mingw: refuse paths containing reserved names,
2019-12-21), we started disallowing file names that are reserved, e.g.
`NUL`, `CONOUT$`, etc.
This included `COM<n>` where `<n>` is a digit. Unfortunately, this
includes `COM0` but only `COM1`, ..., `COM9` are reserved, according to
the official documentation, `COM0` is mentioned in the "NT Namespaces"
section but it is explicitly _omitted_ from the list of reserved names:
https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#naming-conventions
Tests corroborate this: it is totally possible to write a file called
`com0.c` on Windows 10, but not `com1.c`.
So let's tighten the code to disallow only the reserved `COM<n>` file
names, but to allow `COM0` again.
This fixes https://github.com/git-for-windows/git/issues/2470.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-04-08 18:06:49 +00:00
|
|
|
if (c == 'm' || c == 'M') { /* COM1 ... COM9 */
|
|
|
|
c = path[++i];
|
|
|
|
if (c < '1' || c > '9')
|
2019-12-21 22:05:01 +00:00
|
|
|
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;
|
mingw: refuse to access paths with trailing spaces or periods
When creating a directory on Windows whose path ends in a space or a
period (or chains thereof), the Win32 API "helpfully" trims those. For
example, `mkdir("abc ");` will return success, but actually create a
directory called `abc` instead.
This stems back to the DOS days, when all file names had exactly 8
characters plus exactly 3 characters for the file extension, and the
only way to have shorter names was by padding with spaces.
Sadly, this "helpful" behavior is a bit inconsistent: after a successful
`mkdir("abc ");`, a `mkdir("abc /def")` will actually _fail_ (because
the directory `abc ` does not actually exist).
Even if it would work, we now have a serious problem because a Git
repository could contain directories `abc` and `abc `, and on Windows,
they would be "merged" unintentionally.
As these paths are illegal on Windows, anyway, let's disallow any
accesses to such paths on that Operating System.
For practical reasons, this behavior is still guarded by the
config setting `core.protectNTFS`: it is possible (and at least two
regression tests make use of it) to create commits without involving the
worktree. In such a scenario, it is of course possible -- even on
Windows -- to create such file names.
Among other consequences, this patch disallows submodules' paths to end
in spaces on Windows (which would formerly have confused Git enough to
try to write into incorrect paths, anyway).
While this patch does not fix a vulnerability on its own, it prevents an
attack vector that was exploited in demonstrations of a number of
recently-fixed security bugs.
The regression test added to `t/t7417-submodule-path-url.sh` reflects
that attack vector.
Note that we have to adjust the test case "prevent git~1 squatting on
Windows" in `t/t7415-submodule-names.sh` because of a very subtle issue.
It tries to clone two submodules whose names differ only in a trailing
period character, and as a consequence their git directories differ in
the same way. Previously, when Git tried to clone the second submodule,
it thought that the git directory already existed (because on Windows,
when you create a directory with the name `b.` it actually creates `b`),
but with this patch, the first submodule's clone will fail because of
the illegal name of the git directory. Therefore, when cloning the
second submodule, Git will take a different code path: a fresh clone
(without an existing git directory). Both code paths fail to clone the
second submodule, both because the the corresponding worktree directory
exists and is not empty, but the error messages are worded differently.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-05 11:27:53 +00:00
|
|
|
case '.':
|
|
|
|
periods++;
|
|
|
|
/* fallthru */
|
|
|
|
case ' ':
|
|
|
|
preceding_space_or_period = 1;
|
|
|
|
i++;
|
|
|
|
continue;
|
mingw: refuse to access paths with illegal characters
Certain characters are not admissible in file names on Windows, even if
Cygwin/MSYS2 (and therefore, Git for Windows' Bash) pretend that they
are, e.g. `:`, `<`, `>`, etc
Let's disallow those characters explicitly in Windows builds of Git.
Note: just like trailing spaces or periods, it _is_ possible on Windows
to create commits adding files with such illegal characters, as long as
the operation leaves the worktree untouched. To allow for that, we
continue to guard `is_valid_win32_path()` behind the config setting
`core.protectNTFS`, so that users _can_ continue to do that, as long as
they turn the protections off via that config setting.
Among other problems, this prevents Git from trying to write to an "NTFS
Alternate Data Stream" (which refers to metadata stored alongside a
file, under a special name: "<filename>:<stream-name>"). This fix
therefore also prevents an attack vector that was exploited in
demonstrations of a number of recently-fixed security bugs.
Further reading on illegal characters in Win32 filenames:
https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-05 11:44:21 +00:00
|
|
|
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;
|
mingw: refuse to access paths with trailing spaces or periods
When creating a directory on Windows whose path ends in a space or a
period (or chains thereof), the Win32 API "helpfully" trims those. For
example, `mkdir("abc ");` will return success, but actually create a
directory called `abc` instead.
This stems back to the DOS days, when all file names had exactly 8
characters plus exactly 3 characters for the file extension, and the
only way to have shorter names was by padding with spaces.
Sadly, this "helpful" behavior is a bit inconsistent: after a successful
`mkdir("abc ");`, a `mkdir("abc /def")` will actually _fail_ (because
the directory `abc ` does not actually exist).
Even if it would work, we now have a serious problem because a Git
repository could contain directories `abc` and `abc `, and on Windows,
they would be "merged" unintentionally.
As these paths are illegal on Windows, anyway, let's disallow any
accesses to such paths on that Operating System.
For practical reasons, this behavior is still guarded by the
config setting `core.protectNTFS`: it is possible (and at least two
regression tests make use of it) to create commits without involving the
worktree. In such a scenario, it is of course possible -- even on
Windows -- to create such file names.
Among other consequences, this patch disallows submodules' paths to end
in spaces on Windows (which would formerly have confused Git enough to
try to write into incorrect paths, anyway).
While this patch does not fix a vulnerability on its own, it prevents an
attack vector that was exploited in demonstrations of a number of
recently-fixed security bugs.
The regression test added to `t/t7417-submodule-path-url.sh` reflects
that attack vector.
Note that we have to adjust the test case "prevent git~1 squatting on
Windows" in `t/t7415-submodule-names.sh` because of a very subtle issue.
It tries to clone two submodules whose names differ only in a trailing
period character, and as a consequence their git directories differ in
the same way. Previously, when Git tried to clone the second submodule,
it thought that the git directory already existed (because on Windows,
when you create a directory with the name `b.` it actually creates `b`),
but with this patch, the first submodule's clone will fail because of
the illegal name of the git directory. Therefore, when cloning the
second submodule, Git will take a different code path: a fresh clone
(without an existing git directory). Both code paths fail to clone the
second submodule, both because the the corresponding worktree directory
exists and is not empty, but the error messages are worded differently.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
2019-09-05 11:27:53 +00:00
|
|
|
}
|
|
|
|
preceding_space_or_period = 0;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
mingw: replace mingw_startup() hack
Git for Windows has special code to retrieve the command-line parameters
(and even the environment) in UTF-16 encoding, so that they can be
converted to UTF-8. This is necessary because Git for Windows wants to
use UTF-8 encoded strings throughout its code, and the main() function
does not get the parameters in that encoding.
To do that, we used the __wgetmainargs() function, which is not even a
Win32 API function, but provided by the MINGW "runtime" instead.
Obviously, this method would not work with any compiler other than GCC,
and in preparation for compiling with Visual C++, we would like to avoid
precisely that.
Lucky us, there is a much more elegant way: we can simply implement the
UTF-16 variant of `main()`: `wmain()`.
To make that work, we need to link with -municode. The command-line
parameters are passed to `wmain()` encoded in UTF-16, as desired, and
this method also works with GCC, and also with Visual C++ after
adjusting the MSVC linker flags to force it to use `wmain()`.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-06-19 21:05:59 +00:00
|
|
|
#if !defined(_MSC_VER)
|
2011-01-07 18:52:20 +00:00
|
|
|
/*
|
|
|
|
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
|
|
|
|
* mingw startup code, see init.c in mingw runtime).
|
|
|
|
*/
|
|
|
|
int _CRT_glob = 0;
|
mingw: replace mingw_startup() hack
Git for Windows has special code to retrieve the command-line parameters
(and even the environment) in UTF-16 encoding, so that they can be
converted to UTF-8. This is necessary because Git for Windows wants to
use UTF-8 encoded strings throughout its code, and the main() function
does not get the parameters in that encoding.
To do that, we used the __wgetmainargs() function, which is not even a
Win32 API function, but provided by the MINGW "runtime" instead.
Obviously, this method would not work with any compiler other than GCC,
and in preparation for compiling with Visual C++, we would like to avoid
precisely that.
Lucky us, there is a much more elegant way: we can simply implement the
UTF-16 variant of `main()`: `wmain()`.
To make that work, we need to link with -municode. The command-line
parameters are passed to `wmain()` encoded in UTF-16, as desired, and
this method also works with GCC, and also with Visual C++ after
adjusting the MSVC linker flags to force it to use `wmain()`.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-06-19 21:05:59 +00:00
|
|
|
#endif
|
2011-01-16 17:28:27 +00:00
|
|
|
|
2016-06-18 12:38:36 +00:00
|
|
|
static NORETURN void die_startup(void)
|
2011-01-16 17:28:27 +00:00
|
|
|
{
|
|
|
|
fputs("fatal: not enough memory for initialization", stderr);
|
|
|
|
exit(128);
|
|
|
|
}
|
|
|
|
|
2014-07-17 15:38:03 +00:00
|
|
|
static void *malloc_startup(size_t size)
|
|
|
|
{
|
|
|
|
void *result = malloc(size);
|
|
|
|
if (!result)
|
|
|
|
die_startup();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static char *wcstoutfdup_startup(char *buffer, const wchar_t *wcs, size_t len)
|
|
|
|
{
|
|
|
|
len = xwcstoutf(buffer, wcs, len) + 1;
|
|
|
|
return memcpy(malloc_startup(len), buffer, len);
|
|
|
|
}
|
|
|
|
|
2017-11-01 17:10:25 +00:00
|
|
|
static void maybe_redirect_std_handle(const wchar_t *key, DWORD std_id, int fd,
|
|
|
|
DWORD desired_access, DWORD flags)
|
|
|
|
{
|
|
|
|
DWORD create_flag = fd ? OPEN_ALWAYS : OPEN_EXISTING;
|
|
|
|
wchar_t buf[MAX_PATH];
|
|
|
|
DWORD max = ARRAY_SIZE(buf);
|
|
|
|
HANDLE handle;
|
|
|
|
DWORD ret = GetEnvironmentVariableW(key, buf, max);
|
|
|
|
|
|
|
|
if (!ret || ret >= max)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* make sure this does not leak into child processes */
|
|
|
|
SetEnvironmentVariableW(key, NULL);
|
|
|
|
if (!wcscmp(buf, L"off")) {
|
|
|
|
close(fd);
|
|
|
|
handle = GetStdHandle(std_id);
|
|
|
|
if (handle != INVALID_HANDLE_VALUE)
|
|
|
|
CloseHandle(handle);
|
|
|
|
return;
|
|
|
|
}
|
2017-11-01 17:10:30 +00:00
|
|
|
if (std_id == STD_ERROR_HANDLE && !wcscmp(buf, L"2>&1")) {
|
|
|
|
handle = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
if (handle == INVALID_HANDLE_VALUE) {
|
|
|
|
close(fd);
|
|
|
|
handle = GetStdHandle(std_id);
|
|
|
|
if (handle != INVALID_HANDLE_VALUE)
|
|
|
|
CloseHandle(handle);
|
|
|
|
} else {
|
|
|
|
int new_fd = _open_osfhandle((intptr_t)handle, O_BINARY);
|
|
|
|
SetStdHandle(std_id, handle);
|
|
|
|
dup2(new_fd, fd);
|
|
|
|
/* do *not* close the new_fd: that would close stdout */
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2017-11-01 17:10:25 +00:00
|
|
|
handle = CreateFileW(buf, desired_access, 0, NULL, create_flag,
|
|
|
|
flags, NULL);
|
|
|
|
if (handle != INVALID_HANDLE_VALUE) {
|
|
|
|
int new_fd = _open_osfhandle((intptr_t)handle, O_BINARY);
|
|
|
|
SetStdHandle(std_id, handle);
|
|
|
|
dup2(new_fd, fd);
|
|
|
|
close(new_fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void maybe_redirect_std_handles(void)
|
|
|
|
{
|
|
|
|
maybe_redirect_std_handle(L"GIT_REDIRECT_STDIN", STD_INPUT_HANDLE, 0,
|
|
|
|
GENERIC_READ, FILE_ATTRIBUTE_NORMAL);
|
|
|
|
maybe_redirect_std_handle(L"GIT_REDIRECT_STDOUT", STD_OUTPUT_HANDLE, 1,
|
|
|
|
GENERIC_WRITE, FILE_ATTRIBUTE_NORMAL);
|
|
|
|
maybe_redirect_std_handle(L"GIT_REDIRECT_STDERR", STD_ERROR_HANDLE, 2,
|
|
|
|
GENERIC_WRITE, FILE_FLAG_NO_BUFFERING);
|
|
|
|
}
|
|
|
|
|
2019-06-25 14:49:39 +00:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
#ifdef _DEBUG
|
|
|
|
#include <crtdbg.h>
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
mingw: replace mingw_startup() hack
Git for Windows has special code to retrieve the command-line parameters
(and even the environment) in UTF-16 encoding, so that they can be
converted to UTF-8. This is necessary because Git for Windows wants to
use UTF-8 encoded strings throughout its code, and the main() function
does not get the parameters in that encoding.
To do that, we used the __wgetmainargs() function, which is not even a
Win32 API function, but provided by the MINGW "runtime" instead.
Obviously, this method would not work with any compiler other than GCC,
and in preparation for compiling with Visual C++, we would like to avoid
precisely that.
Lucky us, there is a much more elegant way: we can simply implement the
UTF-16 variant of `main()`: `wmain()`.
To make that work, we need to link with -municode. The command-line
parameters are passed to `wmain()` encoded in UTF-16, as desired, and
this method also works with GCC, and also with Visual C++ after
adjusting the MSVC linker flags to force it to use `wmain()`.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-06-19 21:05:59 +00:00
|
|
|
/*
|
|
|
|
* 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)
|
2011-01-07 18:47:23 +00:00
|
|
|
{
|
mingw: replace mingw_startup() hack
Git for Windows has special code to retrieve the command-line parameters
(and even the environment) in UTF-16 encoding, so that they can be
converted to UTF-8. This is necessary because Git for Windows wants to
use UTF-8 encoded strings throughout its code, and the main() function
does not get the parameters in that encoding.
To do that, we used the __wgetmainargs() function, which is not even a
Win32 API function, but provided by the MINGW "runtime" instead.
Obviously, this method would not work with any compiler other than GCC,
and in preparation for compiling with Visual C++, we would like to avoid
precisely that.
Lucky us, there is a much more elegant way: we can simply implement the
UTF-16 variant of `main()`: `wmain()`.
To make that work, we need to link with -municode. The command-line
parameters are passed to `wmain()` encoded in UTF-16, as desired, and
this method also works with GCC, and also with Visual C++ after
adjusting the MSVC linker flags to force it to use `wmain()`.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-06-19 21:05:59 +00:00
|
|
|
int i, maxlen, exit_status;
|
|
|
|
char *buffer, **save;
|
|
|
|
const char **argv;
|
2011-01-16 17:28:27 +00:00
|
|
|
|
2019-04-15 20:39:43 +00:00
|
|
|
trace2_initialize_clock();
|
|
|
|
|
2019-06-25 14:49:40 +00:00
|
|
|
#ifdef _MSC_VER
|
2019-06-25 14:49:42 +00:00
|
|
|
#ifdef _DEBUG
|
|
|
|
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG);
|
|
|
|
#endif
|
|
|
|
|
2019-06-25 14:49:40 +00:00
|
|
|
#ifdef USE_MSVC_CRTDBG
|
|
|
|
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2017-11-01 17:10:25 +00:00
|
|
|
maybe_redirect_std_handles();
|
|
|
|
|
2011-01-16 17:28:27 +00:00
|
|
|
/* determine size of argv and environ conversion buffer */
|
2018-04-10 15:05:46 +00:00
|
|
|
maxlen = wcslen(wargv[0]);
|
2011-01-16 17:28:27 +00:00
|
|
|
for (i = 1; i < argc; i++)
|
|
|
|
maxlen = max(maxlen, wcslen(wargv[i]));
|
|
|
|
|
|
|
|
/* allocate buffer (wchar_t encodes to max 3 UTF-8 bytes) */
|
|
|
|
maxlen = 3 * maxlen + 1;
|
2014-07-17 15:38:03 +00:00
|
|
|
buffer = malloc_startup(maxlen);
|
2011-01-16 17:28:27 +00:00
|
|
|
|
mingw: replace mingw_startup() hack
Git for Windows has special code to retrieve the command-line parameters
(and even the environment) in UTF-16 encoding, so that they can be
converted to UTF-8. This is necessary because Git for Windows wants to
use UTF-8 encoded strings throughout its code, and the main() function
does not get the parameters in that encoding.
To do that, we used the __wgetmainargs() function, which is not even a
Win32 API function, but provided by the MINGW "runtime" instead.
Obviously, this method would not work with any compiler other than GCC,
and in preparation for compiling with Visual C++, we would like to avoid
precisely that.
Lucky us, there is a much more elegant way: we can simply implement the
UTF-16 variant of `main()`: `wmain()`.
To make that work, we need to link with -municode. The command-line
parameters are passed to `wmain()` encoded in UTF-16, as desired, and
this method also works with GCC, and also with Visual C++ after
adjusting the MSVC linker flags to force it to use `wmain()`.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-06-19 21:05:59 +00:00
|
|
|
/*
|
|
|
|
* 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);
|
2018-04-10 15:05:46 +00:00
|
|
|
for (i = 0; i < argc; i++)
|
mingw: replace mingw_startup() hack
Git for Windows has special code to retrieve the command-line parameters
(and even the environment) in UTF-16 encoding, so that they can be
converted to UTF-8. This is necessary because Git for Windows wants to
use UTF-8 encoded strings throughout its code, and the main() function
does not get the parameters in that encoding.
To do that, we used the __wgetmainargs() function, which is not even a
Win32 API function, but provided by the MINGW "runtime" instead.
Obviously, this method would not work with any compiler other than GCC,
and in preparation for compiling with Visual C++, we would like to avoid
precisely that.
Lucky us, there is a much more elegant way: we can simply implement the
UTF-16 variant of `main()`: `wmain()`.
To make that work, we need to link with -municode. The command-line
parameters are passed to `wmain()` encoded in UTF-16, as desired, and
this method also works with GCC, and also with Visual C++ after
adjusting the MSVC linker flags to force it to use `wmain()`.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-06-19 21:05:59 +00:00
|
|
|
argv[i] = save[i] = wcstoutfdup_startup(buffer, wargv[i], maxlen);
|
|
|
|
argv[i] = save[i] = NULL;
|
2011-01-16 17:28:27 +00:00
|
|
|
free(buffer);
|
2011-01-07 18:47:23 +00:00
|
|
|
|
2014-07-17 15:38:05 +00:00
|
|
|
/* fix Windows specific environment settings */
|
2016-01-26 14:34:38 +00:00
|
|
|
setup_windows_environment();
|
2014-07-17 15:38:06 +00:00
|
|
|
|
2018-10-30 18:40:07 +00:00
|
|
|
unset_environment_variables = xstrdup("PERL5LIB");
|
|
|
|
|
2011-01-07 18:47:23 +00:00
|
|
|
/* initialize critical section for waitpid pinfo_t list */
|
|
|
|
InitializeCriticalSection(&pinfo_cs);
|
|
|
|
|
|
|
|
/* set up default file mode and file modes for stdin/out/err */
|
|
|
|
_fmode = _O_BINARY;
|
|
|
|
_setmode(_fileno(stdin), _O_BINARY);
|
|
|
|
_setmode(_fileno(stdout), _O_BINARY);
|
|
|
|
_setmode(_fileno(stderr), _O_BINARY);
|
Win32: Thread-safe windows console output
Winansi.c has many static variables that are accessed and modified from
the [v][f]printf / fputs functions overridden in the file. This may cause
multi threaded git commands that print to the console to produce corrupted
output or even crash.
Additionally, winansi.c doesn't override all functions that can be used to
print to the console (e.g. fwrite, write, fputc are missing), so that ANSI
escapes don't work properly for some git commands (e.g. git-grep).
Instead of doing ANSI emulation in just a few wrapped functions on top of
the IO API, let's plug into the IO system and take advantage of the thread
safety inherent to the IO system.
Redirect stdout and stderr to a pipe if they point to the console. A
background thread reads from the pipe, handles ANSI escape sequences and
UTF-8 to UTF-16 conversion, then writes to the console.
The pipe-based stdout and stderr replacements must be set to unbuffered, as
MSVCRT doesn't support line buffering and fully buffered streams are
inappropriate for console output.
Due to the byte-oriented pipe, ANSI escape sequences and multi-byte UTF-8
sequences can no longer be expected to arrive in one piece. Replace the
string-based ansi_emulate() with a simple stateful parser (this also fixes
colored diff hunk headers, which were broken as of commit 2efcc977).
Override isatty to return true for the pipes redirecting to the console.
Exec/spawn obtain the original console handle to pass to the next process
via winansi_get_osfhandle().
All other overrides are gone, the default stdio implementations work as
expected with the piped stdout/stderr descriptors.
Global variables are either initialized on startup (single threaded) or
exclusively modified by the background thread. Threads communicate through
the pipe, no further synchronization is necessary.
The background thread is terminated by disonnecting the pipe after flushing
the stdio and pipe buffers. This doesn't work for anonymous pipes (created
via CreatePipe), as DisconnectNamedPipe only works on the read end, which
discards remaining data. Thus we have to setup the pipe manually, with the
write end beeing the server (opened with CreateNamedPipe) and the read end
the client (opened with CreateFile).
Limitations: doesn't track reopened or duped file descriptors, i.e.:
- fdopen(1/2) returns fully buffered streams
- dup(1/2), dup2(1/2) returns normal pipe descriptors (i.e. isatty() =
false, winansi_get_osfhandle won't return the original console handle)
Currently, only the git-format-patch command uses xfdopen(xdup(1)) (see
"realstdout" in builtin/log.c), but works well with these limitations.
Many thanks to Atsushi Nakagawa <atnak@chejz.com> for suggesting and
reviewing the thread-exit-mechanism.
Signed-off-by: Karsten Blees <blees@dcon.de>
Signed-off-by: Stepan Kasal <kasal@ucw.cz>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2012-01-14 21:24:19 +00:00
|
|
|
|
|
|
|
/* initialize Unicode console */
|
|
|
|
winansi_init();
|
mingw: replace mingw_startup() hack
Git for Windows has special code to retrieve the command-line parameters
(and even the environment) in UTF-16 encoding, so that they can be
converted to UTF-8. This is necessary because Git for Windows wants to
use UTF-8 encoded strings throughout its code, and the main() function
does not get the parameters in that encoding.
To do that, we used the __wgetmainargs() function, which is not even a
Win32 API function, but provided by the MINGW "runtime" instead.
Obviously, this method would not work with any compiler other than GCC,
and in preparation for compiling with Visual C++, we would like to avoid
precisely that.
Lucky us, there is a much more elegant way: we can simply implement the
UTF-16 variant of `main()`: `wmain()`.
To make that work, we need to link with -municode. The command-line
parameters are passed to `wmain()` encoded in UTF-16, as desired, and
this method also works with GCC, and also with Visual C++ after
adjusting the MSVC linker flags to force it to use `wmain()`.
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2019-06-19 21:05:59 +00:00
|
|
|
|
|
|
|
/* 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;
|
2011-01-07 18:47:23 +00:00
|
|
|
}
|
2015-03-08 10:12:45 +00:00
|
|
|
|
|
|
|
int uname(struct utsname *buf)
|
|
|
|
{
|
2015-10-23 06:02:52 +00:00
|
|
|
unsigned v = (unsigned)GetVersion();
|
2015-03-08 10:12:45 +00:00
|
|
|
memset(buf, 0, sizeof(*buf));
|
2015-09-24 21:06:08 +00:00
|
|
|
xsnprintf(buf->sysname, sizeof(buf->sysname), "Windows");
|
|
|
|
xsnprintf(buf->release, sizeof(buf->release),
|
|
|
|
"%u.%u", v & 0xff, (v >> 8) & 0xff);
|
2015-03-08 10:12:45 +00:00
|
|
|
/* assuming NT variants only.. */
|
2015-09-24 21:06:08 +00:00
|
|
|
xsnprintf(buf->version, sizeof(buf->version),
|
|
|
|
"%u", (v >> 16) & 0x7fff);
|
2015-03-08 10:12:45 +00:00
|
|
|
return 0;
|
|
|
|
}
|