Skip to content

Commit

Permalink
mingw: work around file sharing violations with open()
Browse files Browse the repository at this point in the history
As Git is rooted firmly in the Linux world, its code is ill-prepared for
Windows' file locking semantics: When a process opens a file for
writing, another process cannot open said file at the same time.

This surfaces as a quite big problem in the new test case t0610.47 ("ref
transaction: many concurrent writers") that was introduced in
6241ce2 (refs/reftable: reload locked stack when preparing
transaction, 2024-09-24), i.e. via `ps/reftable-concurrent-writes`.

In the reftable code where it tries to lock the reftable list file by
creating a `tables.list.lock` file in order to start a transaction,
there is a spin lock that expects the `open()` call to either succeed or
to error out with `EEXIST` (when another process already has created
that `tables.list.lock` file, in which case it spins before trying
again).

On Windows, this seems to run into the problem that opening that file
can easily result in `ERROR_SHARING_VIOLATION` instead of the expected
`ERROR_FILE_EXISTS`, resulting in an `EPERM` instead of the expected
`EEXIST`, and the card house of assumptions breaks down.

Now, we cannot simply treat `EPERM` the same as `EEXIST` here because it
is quite possible that a user runs a `git update-ref` in a repository by
mistake where they lack write permissions.

The problem, therefore, is that Git has no proper abstraction layer
where it could discern between `ERROR_SHARING_VIOLATION` and
`ERROR_ACCESS_DENIED`, and keeps trying to use the POSIX functions and
error codes as said abstraction layer.

The work-around I found is to add _another_ spin lock in `mingw_open()`
where it specifically handles the `ERROR_SHARING_VIOLATION` in the same
way already-existing work-arounds in `compat/mingw.c` handle these file
locking issues: keep trying for a while, with increasing delays, and if
all else fails, ask the user whether to try again (if running
interactively) or to give up.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
  • Loading branch information
dscho committed Oct 4, 2024
1 parent a799b99 commit 3903e5d
Showing 1 changed file with 6 additions and 1 deletion.
7 changes: 6 additions & 1 deletion compat/mingw.c
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,7 @@ int mingw_open (const char *filename, int oflags, ...)
int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
wchar_t wfilename[MAX_LONG_PATH];
open_fn_t open_fn;
int tries = 0;

va_start(args, oflags);
mode = va_arg(args, int);
Expand Down Expand Up @@ -813,7 +814,11 @@ int mingw_open (const char *filename, int oflags, ...)
else if (xutftowcs_long_path(wfilename, filename) < 0)
return -1;

fd = open_fn(wfilename, oflags, mode);
do {
fd = open_fn(wfilename, oflags, mode);
} while (fd < 0 && GetLastError() == ERROR_SHARING_VIOLATION &&
retry_ask_yes_no(&tries, "Opening '%s' failed because another "
"application accessed it. Try again?", filename));

if ((oflags & O_CREAT) && fd >= 0 && are_wsl_compatible_mode_bits_enabled()) {
_mode_t wsl_mode = S_IFREG | (mode&0777);
Expand Down

0 comments on commit 3903e5d

Please sign in to comment.