Skip to content

Commit

Permalink
lstat(mingw): correctly detect ENOTDIR scenarios
Browse files Browse the repository at this point in the history
Network shares sometimes use aggressive caching, in which case a
just-created directory might not be immediately visible to Git. In such
an instance, `GetFileAttributesW()` might return a value that
`mingw_lstat()` currently mistakes for not indicating a directory.

One symptom of this scenario is the following error:

	$ git tag -a -m "automatic tag creation"  test_dir/test_tag
	fatal: cannot lock ref 'refs/tags/test_dir/test_tag': unable to resolve reference 'refs/tags/test_dir/test_tag': Not a directory

Note: This does not necessarily happen in all Windows setups. One setup
where it _did_ happen is a Windows Server 2019 VM, and as hinted in

	http://woshub.com/slow-network-shared-folder-refresh-windows-server/

the following commands worked around it:

	Set-SmbClientConfiguration -DirectoryCacheLifetime 0
	Set-SmbClientConfiguration -FileInfoCacheLifetime 0
	Set-SmbClientConfiguration -FileNotFoundCacheLifetime 0

This would impact performance negatively, though, as it essentially
turns off all caching, therefore we do not want to require users to do
that just to be able to use Git on Windows.

The underlying bug is in the code added in 4b0abd5 (mingw: let
lstat() fail with errno == ENOTDIR when appropriate, 2016-01-26) that
emulates the POSIX behavior where `lstat()` should return `ENOENT` if
the file or directory simply does not exist but could be created, and
`ENOTDIR` if there is no file or directory nor could there be because a
leading path already exists and is not a directory.

In that code, the return value of `GetFileAttributesW()` is not
interpreted correctly, so that a perfectly fine leading directory is
misdetected as "not a directory".

As a consequence, the `read_refs_internal()` function would return
`ENOTDIR`, suggesting not only that the tag in the `git tag` invocation
above does not exist, but that it cannot even be created.

Let's fix the code so that it interprets the return value of the
`GetFileAtrtibutesW()` call correctly.

This fixes git-for-windows#3727

Co-authored-by: Pierre Garnier <pgarnier@mega.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
  • Loading branch information
dscho and pgrmega committed Jul 16, 2022
1 parent 4b0abd5 commit de54661
Showing 1 changed file with 2 additions and 2 deletions.
4 changes: 2 additions & 2 deletions compat/mingw.c
Original file line number Diff line number Diff line change
Expand Up @@ -471,8 +471,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
wfilename[n] = L'\0';
attributes = GetFileAttributesW(wfilename);
wfilename[n] = c;
if (attributes == FILE_ATTRIBUTE_DIRECTORY ||
attributes == FILE_ATTRIBUTE_DEVICE)
if (attributes &
(FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE))
return 1;
if (attributes == INVALID_FILE_ATTRIBUTES)
switch (GetLastError()) {
Expand Down

0 comments on commit de54661

Please sign in to comment.