Skip to content

Commit

Permalink
mingw: enable atomic O_APPEND
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
j6t authored and gitster committed Aug 13, 2018
1 parent 53f9a3e commit d641097
Showing 1 changed file with 39 additions and 2 deletions.
41 changes: 39 additions & 2 deletions compat/mingw.c
Original file line number Diff line number Diff line change
Expand Up @@ -341,12 +341,44 @@ int mingw_mkdir(const char *path, int mode)
return ret;
}

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);
if (handle == INVALID_HANDLE_VALUE)
return errno = err_win_to_posix(GetLastError()), -1;
/*
* No O_APPEND here, because the CRT uses it only to reset the
* file pointer to EOF on write(); but that is not necessary
* for a file created with FILE_APPEND_DATA.
*/
fd = _open_osfhandle((intptr_t)handle, O_BINARY);
if (fd < 0)
CloseHandle(handle);
return fd;
}

int mingw_open (const char *filename, int oflags, ...)
{
typedef int (*open_fn_t)(wchar_t const *wfilename, int oflags, ...);
va_list args;
unsigned mode;
int fd;
wchar_t wfilename[MAX_PATH];
open_fn_t open_fn;

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

if (oflags & O_APPEND)
open_fn = mingw_open_append;
else
open_fn = _wopen;

if (xutftowcs_path(wfilename, filename) < 0)
return -1;
fd = _wopen(wfilename, oflags, mode);
fd = open_fn(wfilename, oflags, mode);

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

0 comments on commit d641097

Please sign in to comment.