Skip to content

Commit d641097

Browse files
j6tgitster
authored andcommitted
mingw: enable atomic O_APPEND
The Windows CRT implements O_APPEND "manually": on write() calls, the file pointer is set to EOF before the data is written. Clearly, this is not atomic. And in fact, this is the root cause of failures observed in t5552-skipping-fetch-negotiator.sh and t5503-tagfollow.sh, where different processes write to the same trace file simultanously; it also occurred in t5400-send-pack.sh, but there it was worked around in 71406ed ("t5400: avoid concurrent writes into a trace file", 2017-05-18). Fortunately, Windows does support atomic O_APPEND semantics using the file access mode FILE_APPEND_DATA. Provide an implementation that does. This implementation is minimal in such a way that it only implements the open modes that are actually used in the Git code base. Emulation for other modes can be added as necessary later. To become aware of the necessity early, the unusal error ENOSYS is reported if an unsupported mode is encountered. Diagnosed-by: Johannes Schindelin <[email protected]> Helped-by: Jeff Hostetler <[email protected]> Signed-off-by: Johannes Sixt <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 53f9a3e commit d641097

File tree

1 file changed

+39
-2
lines changed

1 file changed

+39
-2
lines changed

compat/mingw.c

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

344+
static int mingw_open_append(wchar_t const *wfilename, int oflags, ...)
345+
{
346+
HANDLE handle;
347+
int fd;
348+
DWORD create = (oflags & O_CREAT) ? OPEN_ALWAYS : OPEN_EXISTING;
349+
350+
/* only these flags are supported */
351+
if ((oflags & ~O_CREAT) != (O_WRONLY | O_APPEND))
352+
return errno = ENOSYS, -1;
353+
354+
/*
355+
* FILE_SHARE_WRITE is required to permit child processes
356+
* to append to the file.
357+
*/
358+
handle = CreateFileW(wfilename, FILE_APPEND_DATA,
359+
FILE_SHARE_WRITE | FILE_SHARE_READ,
360+
NULL, create, FILE_ATTRIBUTE_NORMAL, NULL);
361+
if (handle == INVALID_HANDLE_VALUE)
362+
return errno = err_win_to_posix(GetLastError()), -1;
363+
/*
364+
* 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.
367+
*/
368+
fd = _open_osfhandle((intptr_t)handle, O_BINARY);
369+
if (fd < 0)
370+
CloseHandle(handle);
371+
return fd;
372+
}
373+
344374
int mingw_open (const char *filename, int oflags, ...)
345375
{
376+
typedef int (*open_fn_t)(wchar_t const *wfilename, int oflags, ...);
346377
va_list args;
347378
unsigned mode;
348379
int fd;
349380
wchar_t wfilename[MAX_PATH];
381+
open_fn_t open_fn;
350382

351383
va_start(args, oflags);
352384
mode = va_arg(args, int);
@@ -355,9 +387,14 @@ int mingw_open (const char *filename, int oflags, ...)
355387
if (filename && !strcmp(filename, "/dev/null"))
356388
filename = "nul";
357389

390+
if (oflags & O_APPEND)
391+
open_fn = mingw_open_append;
392+
else
393+
open_fn = _wopen;
394+
358395
if (xutftowcs_path(wfilename, filename) < 0)
359396
return -1;
360-
fd = _wopen(wfilename, oflags, mode);
397+
fd = open_fn(wfilename, oflags, mode);
361398

362399
if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
363400
DWORD attrs = GetFileAttributesW(wfilename);
@@ -375,7 +412,7 @@ int mingw_open (const char *filename, int oflags, ...)
375412
* CREATE_ALWAYS flag of CreateFile()).
376413
*/
377414
if (fd < 0 && errno == EACCES)
378-
fd = _wopen(wfilename, oflags & ~O_CREAT, mode);
415+
fd = open_fn(wfilename, oflags & ~O_CREAT, mode);
379416
if (fd >= 0 && set_hidden_flag(wfilename, 1))
380417
warning("could not mark '%s' as hidden.", filename);
381418
}

0 commit comments

Comments
 (0)