Thread (12 messages) 12 messages, 2 authors, 2025-03-31

Re: [PATCH v2 2/2] compat/mingw: fix EACCESS when opening files with `O_CREAT | O_EXCL`

From: Patrick Steinhardt <hidden>
Date: 2025-03-31 06:57:38

On Fri, Mar 28, 2025 at 04:41:05PM +0100, Johannes Schindelin wrote:
On Fri, 28 Mar 2025, Patrick Steinhardt wrote:
quoted
On Wed, Mar 26, 2025 at 01:20:12PM +0100, Johannes Schindelin wrote:
quoted
I know that e.g. PostgreSQL used this undocumented function at least at
some stage, but SQLite avoided it by introducing a simple poll strategy.
We could also do that, but if there is already code in the reftable
library that skips doing things if a `.lock` file exists, then doing the
same if the `.lock` file cannot be created, too, should be a safe argument
to make.
I did stumble over the PostgreSQL patch at one point indeed, yeah.

Thanks for the pointer to SQLite. It indeed has the following snippet:

    #define winIoerrCanRetry1(a) (((a)==ERROR_ACCESS_DENIED)        || \
                                  ((a)==ERROR_SHARING_VIOLATION)    || \
                                  ((a)==ERROR_LOCK_VIOLATION)       || \
                                  ((a)==ERROR_DEV_NOT_EXIST)        || \
                                  ((a)==ERROR_NETNAME_DELETED)      || \
                                  ((a)==ERROR_SEM_TIMEOUT)          || \
                                  ((a)==ERROR_NETWORK_UNREACHABLE))

The function gets used via `winRetryIoerr()`, which is used in various
I/O functions to retry the operation, including `winOpen()` to open or
create a file. And it indeed uses a rather simple polling system there
where it sleeps for 25ms up to 10 times.

This certainly is something we could implement in `mingw_open()`: when
we see that `CreateFileW()` has returned any of the above errors we
simply retry the operation. It wouldn't fix the race itself, but it
would hopefully make it less likely to hit. If you would be okay with
such a solution I can implement it.

Also, one thing to note: this problem isn't caused by the reftable
library, it's caused by the lockfile subsystem. So if we don't want to
do this in `mingw_open()`, any self-contained fix should go into the
lockfile system, not into the reftable library, because we may hit the
same symptoms anywhere else where we race around creation/deletion of a
lockfile. We just happen to hit this case in the reftable library
because the test is intentionally stress-testing and racing this code
path.
As I mentioned, I had hoped that we could address this at another layer.

But let's move forward with the `RtlGetLastNtStatus()` solution because,
as you correctly pointed out, it is the only solution so far that lets Git
determine precisely whether the underlying problem is a pending delete.

I had only one remaining concern: If `RtlGetLastNtStatus()` has not yet
been initialized, would we not potentially overwrite the last NTSTATUS
while initializing it? And the answer I can give to myself is: unlikely.
The `ntdll` is already loaded, so there won't be an update to the
`NTSTATUS` there, likewise the `GetProcAddress()` call won't fail and
hence also not update it.

So let's go ahead with v2!
Great, thanks a lot for your expertise and guidance!

Patrick
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help