mingw: add experimental feature to redirect standard handles

On Windows, file handles need to be marked inheritable when they need to
be used as standard input/output/error handles for a newly spawned
process. The problem with that, of course, is that the "inheritable" flag
is global and therefore can wreak havoc with highly multi-threaded
applications: other spawned processes will *also* inherit those file
handles, despite having *other* input/output/error handles, and never
close the former handles because they do not know about them.

Let's introduce a set of environment variables (GIT_REDIRECT_STDIN and
friends) that point to files, or even better, named pipes and that are
used by the spawned Git process. This helps work around above-mentioned
issue: those named pipes will be opened in a non-inheritable way upon
startup, and no handles are passed around.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
Johannes Schindelin
2017-01-10 21:53:00 +01:00
parent 656f071b2a
commit 0e6b6babe9
2 changed files with 49 additions and 0 deletions

View File

@@ -2139,6 +2139,47 @@ static char *wcstoutfdup_startup(char *buffer, const wchar_t *wcs, size_t len)
return memcpy(malloc_startup(len), buffer, len);
}
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_LONG_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;
}
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);
}
void mingw_startup(void)
{
int i, maxlen, argc;
@@ -2146,6 +2187,8 @@ void mingw_startup(void)
wchar_t **wenv, **wargv;
_startupinfo si;
maybe_redirect_std_handles();
/* get wide char arguments and environment */
si.newmode = 0;
if (__wgetmainargs(&argc, &wargv, &wenv, _CRT_glob, &si) < 0)

View File

@@ -427,4 +427,10 @@ test_expect_success 're-init from a linked worktree' '
)
'
test_expect_success MINGW 'redirect std handles' '
GIT_REDIRECT_STDOUT=output.txt git rev-parse --git-dir &&
test .git = "$(cat output.txt)" &&
test -z "$(GIT_REDIRECT_STDOUT=off git rev-parse --git-dir)"
'
test_done