Skip to content

Commit e3d4ff0

Browse files
committed
Merge branch 'js/mingw-o-append'
Further fix for O_APPEND emulation on Windows * js/mingw-o-append: mingw: fix mingw_open_append to work with named pipes t0051: test GIT_TRACE to a windows named pipe
2 parents ae109a9 + eeaf7dd commit e3d4ff0

File tree

6 files changed

+129
-3
lines changed

6 files changed

+129
-3
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,7 @@ TEST_BUILTINS_OBJS += test-submodule-config.o
740740
TEST_BUILTINS_OBJS += test-subprocess.o
741741
TEST_BUILTINS_OBJS += test-urlmatch-normalization.o
742742
TEST_BUILTINS_OBJS += test-wildmatch.o
743+
TEST_BUILTINS_OBJS += test-windows-named-pipe.o
743744
TEST_BUILTINS_OBJS += test-write-cache.o
744745

745746
TEST_PROGRAMS_NEED_X += test-dump-fsmonitor

compat/mingw.c

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,19 @@ int mingw_mkdir(const char *path, int mode)
341341
return ret;
342342
}
343343

344+
/*
345+
* Calling CreateFile() using FILE_APPEND_DATA and without FILE_WRITE_DATA
346+
* is documented in [1] as opening a writable file handle in append mode.
347+
* (It is believed that) this is atomic since it is maintained by the
348+
* kernel unlike the O_APPEND flag which is racily maintained by the CRT.
349+
*
350+
* [1] https://docs.microsoft.com/en-us/windows/desktop/fileio/file-access-rights-constants
351+
*
352+
* This trick does not appear to work for named pipes. Instead it creates
353+
* a named pipe client handle that cannot be written to. Callers should
354+
* just use the regular _wopen() for them. (And since client handle gets
355+
* bound to a unique server handle, it isn't really an issue.)
356+
*/
344357
static int mingw_open_append(wchar_t const *wfilename, int oflags, ...)
345358
{
346359
HANDLE handle;
@@ -360,17 +373,34 @@ static int mingw_open_append(wchar_t const *wfilename, int oflags, ...)
360373
NULL, create, FILE_ATTRIBUTE_NORMAL, NULL);
361374
if (handle == INVALID_HANDLE_VALUE)
362375
return errno = err_win_to_posix(GetLastError()), -1;
376+
363377
/*
364378
* No O_APPEND here, because the CRT uses it only to reset the
365-
* file pointer to EOF on write(); but that is not necessary
366-
* for a file created with FILE_APPEND_DATA.
379+
* file pointer to EOF before each write(); but that is not
380+
* necessary (and may lead to races) for a file created with
381+
* FILE_APPEND_DATA.
367382
*/
368383
fd = _open_osfhandle((intptr_t)handle, O_BINARY);
369384
if (fd < 0)
370385
CloseHandle(handle);
371386
return fd;
372387
}
373388

389+
/*
390+
* Does the pathname map to the local named pipe filesystem?
391+
* That is, does it have a "//./pipe/" prefix?
392+
*/
393+
static int is_local_named_pipe_path(const char *filename)
394+
{
395+
return (is_dir_sep(filename[0]) &&
396+
is_dir_sep(filename[1]) &&
397+
filename[2] == '.' &&
398+
is_dir_sep(filename[3]) &&
399+
!strncasecmp(filename+4, "pipe", 4) &&
400+
is_dir_sep(filename[8]) &&
401+
filename[9]);
402+
}
403+
374404
int mingw_open (const char *filename, int oflags, ...)
375405
{
376406
typedef int (*open_fn_t)(wchar_t const *wfilename, int oflags, ...);
@@ -387,7 +417,7 @@ int mingw_open (const char *filename, int oflags, ...)
387417
if (filename && !strcmp(filename, "/dev/null"))
388418
filename = "nul";
389419

390-
if (oflags & O_APPEND)
420+
if ((oflags & O_APPEND) && !is_local_named_pipe_path(filename))
391421
open_fn = mingw_open_append;
392422
else
393423
open_fn = _wopen;

t/helper/test-tool.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ static struct test_cmd cmds[] = {
4545
{ "subprocess", cmd__subprocess },
4646
{ "urlmatch-normalization", cmd__urlmatch_normalization },
4747
{ "wildmatch", cmd__wildmatch },
48+
#ifdef GIT_WINDOWS_NATIVE
49+
{ "windows-named-pipe", cmd__windows_named_pipe },
50+
#endif
4851
{ "write-cache", cmd__write_cache },
4952
};
5053

t/helper/test-tool.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ int cmd__submodule_config(int argc, const char **argv);
4141
int cmd__subprocess(int argc, const char **argv);
4242
int cmd__urlmatch_normalization(int argc, const char **argv);
4343
int cmd__wildmatch(int argc, const char **argv);
44+
#ifdef GIT_WINDOWS_NATIVE
45+
int cmd__windows_named_pipe(int argc, const char **argv);
46+
#endif
4447
int cmd__write_cache(int argc, const char **argv);
4548

4649
#endif

t/helper/test-windows-named-pipe.c

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#include "test-tool.h"
2+
#include "git-compat-util.h"
3+
#include "strbuf.h"
4+
5+
#ifdef GIT_WINDOWS_NATIVE
6+
static const char *usage_string = "<pipe-filename>";
7+
8+
#define TEST_BUFSIZE (4096)
9+
10+
int cmd__windows_named_pipe(int argc, const char **argv)
11+
{
12+
const char *filename;
13+
struct strbuf pathname = STRBUF_INIT;
14+
int err;
15+
HANDLE h;
16+
BOOL connected;
17+
char buf[TEST_BUFSIZE + 1];
18+
19+
if (argc < 2)
20+
goto print_usage;
21+
filename = argv[1];
22+
if (strchr(filename, '/') || strchr(filename, '\\'))
23+
goto print_usage;
24+
strbuf_addf(&pathname, "//./pipe/%s", filename);
25+
26+
/*
27+
* Create a single instance of the server side of the named pipe.
28+
* This will allow exactly one client instance to connect to it.
29+
*/
30+
h = CreateNamedPipeA(
31+
pathname.buf,
32+
PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE,
33+
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
34+
PIPE_UNLIMITED_INSTANCES,
35+
TEST_BUFSIZE, TEST_BUFSIZE, 0, NULL);
36+
if (h == INVALID_HANDLE_VALUE) {
37+
err = err_win_to_posix(GetLastError());
38+
fprintf(stderr, "CreateNamedPipe failed: %s\n",
39+
strerror(err));
40+
return err;
41+
}
42+
43+
connected = ConnectNamedPipe(h, NULL)
44+
? TRUE
45+
: (GetLastError() == ERROR_PIPE_CONNECTED);
46+
if (!connected) {
47+
err = err_win_to_posix(GetLastError());
48+
fprintf(stderr, "ConnectNamedPipe failed: %s\n",
49+
strerror(err));
50+
CloseHandle(h);
51+
return err;
52+
}
53+
54+
while (1) {
55+
DWORD nbr;
56+
BOOL success = ReadFile(h, buf, TEST_BUFSIZE, &nbr, NULL);
57+
if (!success || nbr == 0)
58+
break;
59+
buf[nbr] = 0;
60+
61+
write(1, buf, nbr);
62+
}
63+
64+
DisconnectNamedPipe(h);
65+
CloseHandle(h);
66+
return 0;
67+
68+
print_usage:
69+
fprintf(stderr, "usage: %s %s\n", argv[0], usage_string);
70+
return 1;
71+
}
72+
#endif

t/t0051-windows-named-pipe.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/bin/sh
2+
3+
test_description='Windows named pipes'
4+
5+
. ./test-lib.sh
6+
7+
test_expect_success MINGW 'o_append write to named pipe' '
8+
GIT_TRACE="$(pwd)/expect" git status >/dev/null 2>&1 &&
9+
{ test-tool windows-named-pipe t0051 >actual 2>&1 & } &&
10+
pid=$! &&
11+
sleep 1 &&
12+
GIT_TRACE=//./pipe/t0051 git status >/dev/null 2>warning &&
13+
wait $pid &&
14+
test_cmp expect actual
15+
'
16+
17+
test_done

0 commit comments

Comments
 (0)