Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error at tag creation over local network when name includes directory "{dir}/{name}" : cannot lock ref #3727

Closed
1 task done
pgrmega opened this issue Mar 10, 2022 · 16 comments
Labels

Comments

@pgrmega
Copy link

pgrmega commented Mar 10, 2022

  • I was not able to find an open or closed issue matching what I'm seeing

Setup

  • Which version of Git for Windows are you using? Is it 32-bit or 64-bit?
$ git --version --build-options

git version 2.35.1.windows.2
cpu: x86_64
built from commit: 5437f0fd368c7faf1a0b5e1fef048232c1f2a3e6
sizeof-long: 4
sizeof-size_t: 8
shell-path: /bin/sh
feature: fsmonitor--daemon

(please notice that we tested with older versions like Git 2.15.0.)

  • Which version of Windows are you running? Vista, 7, 8, 10? Is it 32-bit or 64-bit?
$ cmd.exe /c ver

Microsoft Windows [Version 10.0.17763.2565]
  • What options did you set as part of the installation? Or did you choose the
    defaults?
# One of the following:
> type "C:\Program Files\Git\etc\install-options.txt"
> type "C:\Program Files (x86)\Git\etc\install-options.txt"
> type "%USERPROFILE%\AppData\Local\Programs\Git\etc\install-options.txt"
$ cat /etc/install-options.txt

Editor Option: Notepad++
Custom Editor Path:
Default Branch Option:
Path Option: Cmd
SSH Option: OpenSSH
Tortoise Option: false
CURL Option: WinSSL
CRLF Option: CRLFCommitAsIs
Bash Terminal Option: ConHost
Git Pull Behavior Option: Merge
Use Credential Manager: Enabled
Performance Tweaks FSCache: Enabled
Enable Symlinks: Disabled
Enable Pseudo Console Support: Disabled
Enable FSMonitor: Disabled

  • Any other interesting things about your environment that might be related
    to the issue you're seeing?

The git repository is not stored on the machine but accessed on a shared directory available into the local network.
The error does not occur on other machines accessing the same shared resource, it is to say the only thing is that we created new machines with a more recent OS (Windows Server 2012R2 => Windows Server 2019), all other things being equal.
The error does not occur with a repository locally stored on the machine hard drive.
The error occurs only for creation of a new tag including a directory : "{dir}/{name}".
It does not occurs if the directory already exists in the ".git\refs\tags".
It does not occurs if we create a tag not including a directory name : "{name}".

Details

  • Which terminal/shell are you running Git from? e.g Bash/CMD/PowerShell/other

We use PowerShell.
We tested on Bash/CMD/Powershell and have the same issue.


PS X:\wa\code-1700_000.dev\relman_tools> 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
PS X:\wa\code-1700_000.dev\relman_tools> 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
PS X:\wa\code-1700_000.dev\relman_tools> git tag -a -m "automatic tag creation"  test_dir/test_tag
PS X:\wa\code-1700_000.dev\relman_tools>

  • What did you expect to occur after running these commands?

We expect the tag to be created.

  • What actually happened instead?

An error : fatal: cannot lock ref 'refs/tags/test_dir/test_tag': unable to resolve reference 'refs/tags/test_dir/test_tag': Not a directory

  • If the problem was occurring with a specific repository, can you provide the
    URL to that repository to help us with testing?

It occurs with any repository.

@dscho
Copy link
Member

dscho commented Mar 10, 2022

fatal: cannot lock ref 'refs/tags/test_dir/test_tag': unable to resolve reference 'refs/tags/test_dir/test_tag': Not a directory

First of all, could you verify that git for-each-ref does not contain refs/tags/test_dir as a tag?

@pgrmega
Copy link
Author

pgrmega commented Mar 10, 2022

Hi,

If I do this just after the error it does not return a line for test_dir.
If I do this after a second try, waiting few seconds, it returns a ref, see belown :

PS X:\wa\code-1700_000.dev\relman_tools> git for-each-ref | ?{ $_ -match "test_" }
87cbf7ebe8af210a3fee234dddfba710a70c40f2 tag    refs/tags/test_dir/test_tag

After the first try, the directory is immediately visible in the file explorer.
But seems that git does not detect it immediately.

@dscho
Copy link
Member

dscho commented Mar 14, 2022

But seems that git does not detect it immediately.

Wait, Explorer sees the update immediately, but git.exe does not? What does dir in a CMD say?

@pgrmega
Copy link
Author

pgrmega commented Mar 15, 2022

What we experimented is that explorer sees the update immediately and git complains with this message : "cannot lock ref 'refs/tags/test_dir/test_tag': unable to resolve reference 'refs/tags/test_dir/test_tag': Not a directory".

Does it means that git does not see the directory immediately or it is another problem ... I can't say. It's a supposition.
CMD => OK
PowerShell => OK

Could it comes from the API used by Git ?

@dscho
Copy link
Member

dscho commented Mar 16, 2022

What does dir in a CMD say?

The answer to that question would still be quite useful.

What we experimented is that explorer sees the update immediately and git complains with this message : "cannot lock ref 'refs/tags/test_dir/test_tag': unable to resolve reference 'refs/tags/test_dir/test_tag': Not a directory".

You could build a custom Git for Windows where you instrument the code that produces that error message to find out in more detail what is going on. The culprit might be that something like EAGAIN needs to be handled, but isn't.

CMD => OK
PowerShell => OK

Are you saying that running dir in those works, directly after creating the directory? Or are you saying that the git command works? Sorry, this is not clear to me.

@dscho dscho added the unclear label Mar 16, 2022
@pgrmega
Copy link
Author

pgrmega commented Mar 16, 2022

I'll try to be clearer by illustrating with a script and the output.

The script (batch file test.bat executed in the CMD terminal) :

x:
cd X:\wa\code-1700_000.dev\relman_tools

for /L %%n in (1,1,3) do (
  echo "------------------------------------------------------------------------
  echo "- ITERATION %%n"
  echo "------------------------------------------------------------------------
  git tag -a -m "automatic tag creation"  test_dir/test_tag
  dir X:\wa\code-1700_000.dev\relman_tools\.git\refs\tags
  timeout 4 > NUL
)

The output :

X:\wa\code-1700_000.dev\relman_tools>(
echo "------------------------------------------------------------------------
 echo "- ITERATION 1"
 echo "------------------------------------------------------------------------
 git tag -a -m "automatic tag creation"  test_dir/test_tag
 dir X:\wa\code-1700_000.dev\relman_tools\.git\refs\tags
 timeout 4  1>NUL
)
"------------------------------------------------------------------------
"- ITERATION 1"
"------------------------------------------------------------------------
fatal: cannot lock ref 'refs/tags/test_dir/test_tag': unable to resolve reference 'refs/tags/test_dir/test_tag': Not a directory
 Volume in drive X is Data
 Volume Serial Number is 9422-CEDE

 Directory of X:\wa\code-1700_000.dev\relman_tools\.git\refs\tags

03/16/2022  12:08 PM    <DIR>          .
03/16/2022  12:08 PM    <DIR>          ..
03/16/2022  12:08 PM    <DIR>          test_dir
               0 File(s)              0 bytes
               3 Dir(s)  1,921,131,634,688 bytes free

X:\wa\code-1700_000.dev\relman_tools>(
echo "------------------------------------------------------------------------
 echo "- ITERATION 2"
 echo "------------------------------------------------------------------------
 git tag -a -m "automatic tag creation"  test_dir/test_tag
 dir X:\wa\code-1700_000.dev\relman_tools\.git\refs\tags
 timeout 4  1>NUL
)
"------------------------------------------------------------------------
"- ITERATION 2"
"------------------------------------------------------------------------
fatal: cannot lock ref 'refs/tags/test_dir/test_tag': unable to resolve reference 'refs/tags/test_dir/test_tag': Not a directory
 Volume in drive X is Data
 Volume Serial Number is 9422-CEDE

 Directory of X:\wa\code-1700_000.dev\relman_tools\.git\refs\tags

03/16/2022  12:08 PM    <DIR>          .
03/16/2022  12:08 PM    <DIR>          ..
03/16/2022  12:08 PM    <DIR>          test_dir
               0 File(s)              0 bytes
               3 Dir(s)  1,921,131,630,592 bytes free

X:\wa\code-1700_000.dev\relman_tools>(
echo "------------------------------------------------------------------------
 echo "- ITERATION 3"
 echo "------------------------------------------------------------------------
 git tag -a -m "automatic tag creation"  test_dir/test_tag
 dir X:\wa\code-1700_000.dev\relman_tools\.git\refs\tags
 timeout 4  1>NUL
)
"------------------------------------------------------------------------
"- ITERATION 3"
"------------------------------------------------------------------------
 Volume in drive X is Data
 Volume Serial Number is 9422-CEDE

 Directory of X:\wa\code-1700_000.dev\relman_tools\.git\refs\tags

03/16/2022  12:08 PM    <DIR>          .
03/16/2022  12:08 PM    <DIR>          ..
03/16/2022  12:08 PM    <DIR>          test_dir
               0 File(s)              0 bytes
               3 Dir(s)  1,921,131,622,400 bytes free

@dscho
Copy link
Member

dscho commented Mar 16, 2022

Okay. I fear that the only way to diagnose this better is the approach I detailed above where you rebuild Git after instrumenting it with debug printf statements.

@pgrmega
Copy link
Author

pgrmega commented Mar 16, 2022

I added a printf to output the error number.
image

It gave me 20 => ENOTDIR.
image

If you have any idea were to add output, can you share a branch with me so that I checkout your changes and test it in my context ?

Thank you !

@dscho
Copy link
Member

dscho commented Mar 16, 2022

It gave me 20 => ENOTDIR.

Yes, that looks correct.

image

If you have any idea were to add output

The most promising path forward would be to follow the code path into files_read_raw_ref(). Something in there sets that errno, and that something might be a function in compat/mingw.c that translates the result of GetLastError() into a POSIX-style errno (and maybe it does so incorrectly).

@pgrmega
Copy link
Author

pgrmega commented Mar 21, 2022

Hello,
I've tried to add some informations to understand what is going on.
What I've done is in this patch :
0001-debug-git-for-windows-git-issue-3727.zip

Before each test I delete the existing tags and tags directories.

By adding a FindFirstFileW before GetFileAttributesExW in mingw.c , the tag creates without git complaining.

$ git tag -a -m "automatic tag creation" test100/test100
[DEBUG] stat_ref_count : 1
[DEBUG] lstat call : .git/HEAD
[DEBUG] lstat > 0
[DEBUG] stat_ref_count : 2
[DEBUG] lstat call : .git/refs/heads/1700_000.dev
[DEBUG] mingw_lstat : .git/refs/heads/1700_000.dev
[DEBUG] mingw_lstat FindFirstFile failed (2)
[DEBUG] mingw_lstat error : 2
[DEBUG] lstat < 0
[DEBUG] lstat errno = 2
[DEBUG] stat_ref_count : 3
[DEBUG] lstat call : .git/refs/HEAD
[DEBUG] mingw_lstat : .git/refs/HEAD
[DEBUG] mingw_lstat FindFirstFile failed (2)
[DEBUG] mingw_lstat error : 2
[DEBUG] lstat < 0
[DEBUG] lstat errno = 2
[DEBUG] stat_ref_count : 4
[DEBUG] lstat call : .git/refs/tags/HEAD
[DEBUG] mingw_lstat : .git/refs/tags/HEAD
[DEBUG] mingw_lstat FindFirstFile failed (2)
[DEBUG] mingw_lstat error : 2
[DEBUG] lstat < 0
[DEBUG] lstat errno = 2
[DEBUG] stat_ref_count : 5
[DEBUG] lstat call : .git/refs/heads/HEAD
[DEBUG] mingw_lstat : .git/refs/heads/HEAD
[DEBUG] mingw_lstat FindFirstFile failed (2)
[DEBUG] mingw_lstat error : 2
[DEBUG] lstat < 0
[DEBUG] lstat errno = 2
[DEBUG] stat_ref_count : 6
[DEBUG] lstat call : .git/refs/remotes/HEAD
[DEBUG] mingw_lstat : .git/refs/remotes/HEAD
[DEBUG] mingw_lstat FindFirstFile failed (2)
[DEBUG] mingw_lstat error : 2
[DEBUG] lstat < 0
[DEBUG] lstat errno = 2
[DEBUG] stat_ref_count : 7
[DEBUG] lstat call : .git/refs/remotes/HEAD/HEAD
[DEBUG] mingw_lstat : .git/refs/remotes/HEAD/HEAD
[DEBUG] mingw_lstat FindFirstFile failed (3)
[DEBUG] mingw_lstat error : 3
[DEBUG] lstat < 0
[DEBUG] lstat errno = 2
[DEBUG] stat_ref_count : 8
[DEBUG] lstat call : .git/refs/tags/test100/test100
[DEBUG] mingw_lstat : .git/refs/tags/test100/test100
[DEBUG] mingw_lstat FindFirstFile failed (3)
[DEBUG] mingw_lstat error : 3
[DEBUG] lstat < 0
[DEBUG] lstat errno = 2
[DEBUG] stat_ref_count : 9
[DEBUG] lstat call : .git/HEAD
[DEBUG] lstat > 0
[DEBUG] stat_ref_count : 10
[DEBUG] lstat call : .git/refs/tags/test100/test100
[DEBUG] mingw_lstat : .git/refs/tags/test100/test100
[DEBUG] mingw_lstat FindFirstFile failed (2)
[DEBUG] mingw_lstat error : 2
[DEBUG] lstat < 0
[DEBUG] lstat errno = 2

If I don't do the FindFirstFileW, I have the error

$ git tag -a -m "automatic tag creation" test100/test100
fatal: cannot lock ref 'refs/tags/test100/test100': unable to resolve reference 'refs/tags/test100/test100': Not a directory
[DEBUG] stat_ref_count : 1
[DEBUG] lstat call : .git/HEAD
[DEBUG] lstat > 0
[DEBUG] stat_ref_count : 2
[DEBUG] lstat call : .git/refs/heads/1700_000.dev
[DEBUG] mingw_lstat : .git/refs/heads/1700_000.dev
[DEBUG] mingw_lstat The first file found is relman_tools
[DEBUG] mingw_lstat error : 2
[DEBUG] lstat < 0
[DEBUG] lstat errno = 2
[DEBUG] stat_ref_count : 3
[DEBUG] lstat call : .git/refs/HEAD
[DEBUG] mingw_lstat : .git/refs/HEAD
[DEBUG] mingw_lstat The first file found is relman_tools
[DEBUG] mingw_lstat error : 2
[DEBUG] lstat < 0
[DEBUG] lstat errno = 2
[DEBUG] stat_ref_count : 4
[DEBUG] lstat call : .git/refs/tags/HEAD
[DEBUG] mingw_lstat : .git/refs/tags/HEAD
[DEBUG] mingw_lstat The first file found is relman_tools
[DEBUG] mingw_lstat error : 2
[DEBUG] lstat < 0
[DEBUG] lstat errno = 2
[DEBUG] stat_ref_count : 5
[DEBUG] lstat call : .git/refs/heads/HEAD
[DEBUG] mingw_lstat : .git/refs/heads/HEAD
[DEBUG] mingw_lstat The first file found is relman_tools
[DEBUG] mingw_lstat error : 2
[DEBUG] lstat < 0
[DEBUG] lstat errno = 2
[DEBUG] stat_ref_count : 6
[DEBUG] lstat call : .git/refs/remotes/HEAD
[DEBUG] mingw_lstat : .git/refs/remotes/HEAD
[DEBUG] mingw_lstat The first file found is relman_tools
[DEBUG] mingw_lstat error : 2
[DEBUG] lstat < 0
[DEBUG] lstat errno = 2
[DEBUG] stat_ref_count : 7
[DEBUG] lstat call : .git/refs/remotes/HEAD/HEAD
[DEBUG] mingw_lstat : .git/refs/remotes/HEAD/HEAD
[DEBUG] mingw_lstat The first file found is relman_tools
[DEBUG] mingw_lstat error : 3
[DEBUG] lstat < 0
[DEBUG] lstat errno = 2
[DEBUG] stat_ref_count : 8
[DEBUG] lstat call : .git/refs/tags/test100/test100
[DEBUG] mingw_lstat : .git/refs/tags/test100/test100
[DEBUG] mingw_lstat The first file found is relman_tools
[DEBUG] mingw_lstat error : 3
[DEBUG] lstat < 0
[DEBUG] lstat errno = 2
[DEBUG] stat_ref_count : 9
[DEBUG] lstat call : .git/HEAD
[DEBUG] lstat > 0
[DEBUG] stat_ref_count : 10
[DEBUG] lstat call : .git/refs/tags/test100/test100
[DEBUG] mingw_lstat : .git/refs/tags/test100/test100
[DEBUG] mingw_lstat The first file found is relman_tools
[DEBUG] mingw_lstat error : 3
[DEBUG] lstat < 0
[DEBUG] lstat errno = 20

If I try to FindFirstFileW with ".", ".git/refs/tags" or ".git/refs/tags/test100", it fails.
If I FindFirstFileW with ".git/refs/tags/test100/test100" it succeeds.

Anyone having any idea ?

Thank you

@pgrmega
Copy link
Author

pgrmega commented Mar 21, 2022

Hi !

After instrumenting in many ways to understand the code and particularly mingw_lstat and has_valid_directory_prefix (in mingw.c), I tried to instrument and debug then write my version of mingw_mkdir.

Finally, I am wondering if the modification to do is to adapt the lstat error management in files_read_raw_ref ?
=> should'nt it manage ENOTDIR error ?

If I replace if (myerr != ENOENT) by if (myerr != ENOENT && myerr != ENOTDIR) in files-backend.c here :

if (myerr != ENOENT)
,
in my use case, it seems to fix the problem.
But I don't know the possible drawbacks or regression of this change...

@pgrmega
Copy link
Author

pgrmega commented Mar 23, 2022

Hello,

I don't know what to do with the result of my analysis.

My conclusion is that the following line consider that any error different from ENOENT leads to the error that I experiment :

if (myerr != ENOENT)

whereas ENOENT is caught by the caller :

git/refs/files-backend.c

Lines 640 to 642 in 158a30d

if (files_read_raw_ref(&refs->base, refname, &lock->old_oid, referent,
type, &failure_errno)) {
if (failure_errno == ENOENT) {

mingw_lstat seems to consider that ERROR_PATH_NOT_FOUND leeds to ENOTDIR when the parent directory exists in the path given in parameter (this is may case when I create a tag in the form "/") :

git/compat/mingw.c

Lines 1059 to 1063 in 158a30d

case ERROR_PATH_NOT_FOUND:
if (!has_valid_directory_prefix(wfilename)) {
errno = ENOTDIR;
break;
}

In the end, it could mean that ENOTDIR should be managed in the caller of lstat, that is to say files_read_raw_ref or lock_raw_ref.

Do you agree ?

Thank you.

@dscho
Copy link
Member

dscho commented Mar 24, 2022

If I replace if (myerr != ENOENT) by if (myerr != ENOENT && myerr != ENOTDIR) in files-backend.c here :

if (myerr != ENOENT)

That sounds like a really good fix!

Would you mind writing up the problem, the context, and the analysis in a commit message and then open a PullRequest?

@pgrmega
Copy link
Author

pgrmega commented Mar 24, 2022

Yes, I will.

@dscho
Copy link
Member

dscho commented Mar 24, 2022

@pgrmega thank you!

pgrmega added a commit to pgrmega/git that referenced this issue Mar 24, 2022
This fix for the issue : git-for-windows#3727

Use case :
Creation of a tag in the form "parent/child".

Exemple :
 > git tag -a -m "my message" tagdir/mytag

Context :
  $ git --version --build-options
    git version 2.35.1.windows.2
    cpu: x86_64
    built from commit: 5437f0f
    sizeof-long: 4
    sizeof-size_t: 8
    shell-path: /bin/sh
    feature: fsmonitor--daemon
  $ cmd.exe /c ver
    Microsoft Windows [Version 10.0.17763.2565]

Error :
  fatal: cannot lock ref 'refs/tags/tagdir/mytag': unable to resolve reference 'refs/tags/tagdir/mytag': Not a directory

Problem analysis:
GetFileAttributesExW used in mingw_lstat function in git/compat/mingw.c can raise an error ERROR_PATH_NOT_FOUND.
In this case, the has_valid_directory_prefix is used to check if the parent directory exists.
So that, when the parent directory exists, mingw_lstat returns ENOTDIR.
ENOTDIR is not managed by the caller code (files_read_raw_ref in git/refs/files-backend.c).
It probably should.

Conclusion
This commit enables to take into account the case when ENOTDIR is returned by mingw_lstat.
pgrmega added a commit to pgrmega/git that referenced this issue Mar 24, 2022
This fix for the issue : git-for-windows#3727

Use case :
Creation of a tag in the form "parent/child".

Exemple :
 > git tag -a -m "my message" tagdir/mytag

Context :
  $ git --version --build-options
    git version 2.35.1.windows.2
    cpu: x86_64
    built from commit: 5437f0f
    sizeof-long: 4
    sizeof-size_t: 8
    shell-path: /bin/sh
    feature: fsmonitor--daemon
  $ cmd.exe /c ver
    Microsoft Windows [Version 10.0.17763.2565]

Error :
  fatal: cannot lock ref 'refs/tags/tagdir/mytag': unable to resolve reference 'refs/tags/tagdir/mytag': Not a directory

Problem analysis:
GetFileAttributesExW used in mingw_lstat function in git/compat/mingw.c can raise an error ERROR_PATH_NOT_FOUND.
In this case, the has_valid_directory_prefix is used to check if the parent directory exists.
So that, when the parent directory exists, mingw_lstat returns ENOTDIR.
ENOTDIR is not managed by the caller code (files_read_raw_ref in git/refs/files-backend.c).
It probably should.

Conclusion
This commit enables to take into account the case when ENOTDIR is returned by mingw_lstat.
@pgrmega
Copy link
Author

pgrmega commented Mar 31, 2022

Hello,

For information...

As I said in the description of the issue, the problem occurs only on our Windows Server 2019 VMs but not on the Windows Server 2012R2.
In parallel of debuging git, which led to the fix I pushed, we had a look on the machine configuration side.

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

After applying those commands (that disable caches), the problem disappeared :

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

pgrmega added a commit to pgrmega/git that referenced this issue Apr 5, 2022
This fix for the issue : git-for-windows#3727

Use case :
Creation of a tag in the form "parent/child".

Exemple :
 > git tag -a -m "my message" tagdir/mytag

Context :
  $ git --version --build-options
    git version 2.35.1.windows.2
    cpu: x86_64
    built from commit: 5437f0f
    sizeof-long: 4
    sizeof-size_t: 8
    shell-path: /bin/sh
    feature: fsmonitor--daemon
  $ cmd.exe /c ver
    Microsoft Windows [Version 10.0.17763.2565]

Error :
  fatal: cannot lock ref 'refs/tags/tagdir/mytag': unable to resolve reference 'refs/tags/tagdir/mytag': Not a directory

Problem analysis:
GetFileAttributesExW used in mingw_lstat function in git/compat/mingw.c can raise an error ERROR_PATH_NOT_FOUND.
In this case, the has_valid_directory_prefix is used to check if the parent directory exists.
So that, when the parent directory exists, mingw_lstat returns ENOTDIR.
ENOTDIR is not managed by the caller code (files_read_raw_ref in git/refs/files-backend.c).
It probably should.

Conclusion
This commit enables to take into account the case when ENOTDIR is returned by mingw_lstat.

Signed-off-by: Pierre Garnier <pgarnier@mega.com>
pgrmega added a commit to pgrmega/git that referenced this issue Apr 5, 2022
This fix for the issue : git-for-windows#3727

Use case :
Creation of a tag in the form "parent/child".

Exemple :
 > git tag -a -m "my message" tagdir/mytag

Context :
  $ git --version --build-options
    git version 2.35.1.windows.2
    cpu: x86_64
    built from commit: 5437f0f
    sizeof-long: 4
    sizeof-size_t: 8
    shell-path: /bin/sh
    feature: fsmonitor--daemon
  $ cmd.exe /c ver
    Microsoft Windows [Version 10.0.17763.2565]

Error :
  fatal: cannot lock ref 'refs/tags/tagdir/mytag': unable to resolve reference 'refs/tags/tagdir/mytag': Not a directory

Problem analysis:
GetFileAttributesExW used in mingw_lstat function in git/compat/mingw.c can raise an error ERROR_PATH_NOT_FOUND.
In this case, the has_valid_directory_prefix is used to check if the parent directory exists.
So that, when the parent directory exists, mingw_lstat returns ENOTDIR.
ENOTDIR is not managed by the caller code (files_read_raw_ref in git/refs/files-backend.c).
It probably should.

Conclusion
This commit enables to take into account the case when ENOTDIR is returned by mingw_lstat.

Signed-off-by: GARNIER Pierre <pgarnier@mega.com>
pgrmega added a commit to pgrmega/git that referenced this issue Apr 5, 2022
This fix for the issue : git-for-windows#3727

Use case :
Creation of a tag in the form "parent/child".

Exemple :
 > git tag -a -m "my message" tagdir/mytag

Context :
  $ git --version --build-options
    git version 2.35.1.windows.2
    cpu: x86_64
    built from commit: 5437f0f
    sizeof-long: 4
    sizeof-size_t: 8
    shell-path: /bin/sh
    feature: fsmonitor--daemon
  $ cmd.exe /c ver
    Microsoft Windows [Version 10.0.17763.2565]

Error :
  fatal: cannot lock ref 'refs/tags/tagdir/mytag': unable to resolve reference 'refs/tags/tagdir/mytag': Not a directory

Problem analysis:
GetFileAttributesExW used in mingw_lstat function in git/compat/mingw.c can raise an error ERROR_PATH_NOT_FOUND.
In this case, the has_valid_directory_prefix is used to check if the parent directory exists.
So that, when the parent directory exists, mingw_lstat returns ENOTDIR.
ENOTDIR is not managed by the caller code (files_read_raw_ref in git/refs/files-backend.c).
It probably should.

Conclusion
This commit enables to take into account the case when ENOTDIR is returned by mingw_lstat.

Signed-off-by: pgrmega <pgrmega@github.com>
pgrmega added a commit to pgrmega/git that referenced this issue Apr 5, 2022
This fix for the issue : git-for-windows#3727

Use case :
Creation of a tag in the form "parent/child".

Exemple :
 > git tag -a -m "my message" tagdir/mytag

Context :
  $ git --version --build-options
    git version 2.35.1.windows.2
    cpu: x86_64
    built from commit: 5437f0f
    sizeof-long: 4
    sizeof-size_t: 8
    shell-path: /bin/sh
    feature: fsmonitor--daemon
  $ cmd.exe /c ver
    Microsoft Windows [Version 10.0.17763.2565]

Error :
  fatal: cannot lock ref 'refs/tags/tagdir/mytag': unable to resolve reference 'refs/tags/tagdir/mytag': Not a directory

Problem analysis:
GetFileAttributesExW used in mingw_lstat function in git/compat/mingw.c can raise an error ERROR_PATH_NOT_FOUND.
In this case, the has_valid_directory_prefix is used to check if the parent directory exists.
So that, when the parent directory exists, mingw_lstat returns ENOTDIR.
ENOTDIR is not managed by the caller code (files_read_raw_ref in git/refs/files-backend.c).
It probably should.

Conclusion
This commit enables to take into account the case when ENOTDIR is returned by mingw_lstat.

Signed-off-by: pgrmega <pgarnier@mega.com>
pgrmega added a commit to pgrmega/git that referenced this issue Apr 8, 2022
This fix for the issue : git-for-windows#3727

Use case :
Creation of a tag in the form "parent/child".

Exemple :
 > git tag -a -m "my message" tagdir/mytag

Context :
  $ git --version --build-options
    git version 2.35.1.windows.2
    cpu: x86_64
    built from commit: 5437f0f
    sizeof-long: 4
    sizeof-size_t: 8
    shell-path: /bin/sh
    feature: fsmonitor--daemon
  $ cmd.exe /c ver
    Microsoft Windows [Version 10.0.17763.2565]

Error :
  fatal: cannot lock ref 'refs/tags/tagdir/mytag': unable to resolve reference 'refs/tags/tagdir/mytag': Not a directory

Problem analysis:
GetFileAttributesExW used in mingw_lstat function in git/compat/mingw.c can raise an error ERROR_PATH_NOT_FOUND.
In this case, the has_valid_directory_prefix is used to check if the parent directory exists.
So that, when the parent directory exists, mingw_lstat returns ENOTDIR.
ENOTDIR is not managed by the caller code (files_read_raw_ref in git/refs/files-backend.c).
It probably should.

Conclusion
This commit enables to take into account the case when ENOTDIR is returned by mingw_lstat.

Signed-off-by: pgrmega <pgarnier@mega.com>
pgrmega added a commit to pgrmega/git that referenced this issue Apr 8, 2022
This fix for the issue : git-for-windows#3727

Use case :
Creation of a tag in the form "parent/child".

Exemple :
 > git tag -a -m "my message" tagdir/mytag

Context :
  $ git --version --build-options
    git version 2.35.1.windows.2
    cpu: x86_64
    built from commit: 5437f0f
    sizeof-long: 4
    sizeof-size_t: 8
    shell-path: /bin/sh
    feature: fsmonitor--daemon
  $ cmd.exe /c ver
    Microsoft Windows [Version 10.0.17763.2565]

Error :
  fatal: cannot lock ref 'refs/tags/tagdir/mytag': unable to resolve reference 'refs/tags/tagdir/mytag': Not a directory

Problem analysis:
GetFileAttributesExW used in mingw_lstat function in git/compat/mingw.c can raise an error ERROR_PATH_NOT_FOUND.
In this case, the has_valid_directory_prefix is used to check if the parent directory exists.
So that, when the parent directory exists, mingw_lstat returns ENOTDIR.
ENOTDIR is not managed by the caller code (files_read_raw_ref in git/refs/files-backend.c).
It probably should.

Conclusion
This commit enables to take into account the case when ENOTDIR is returned by mingw_lstat.

Signed-off-by: pgrmega <pgarnier@mega.com>
pgrmega added a commit to pgrmega/git that referenced this issue Apr 8, 2022
This fix for the issue : git-for-windows#3727

Use case :
Creation of a tag in the form "parent/child".

Exemple :
 > git tag -a -m "my message" tagdir/mytag

Context :
  $ git --version --build-options
    git version 2.35.1.windows.2
    cpu: x86_64
    built from commit: 5437f0f
    sizeof-long: 4
    sizeof-size_t: 8
    shell-path: /bin/sh
    feature: fsmonitor--daemon
  $ cmd.exe /c ver
    Microsoft Windows [Version 10.0.17763.2565]

Error :
  fatal: cannot lock ref 'refs/tags/tagdir/mytag': unable to resolve reference 'refs/tags/tagdir/mytag': Not a directory

Problem analysis:
GetFileAttributesExW used in mingw_lstat function in git/compat/mingw.c can raise an error ERROR_PATH_NOT_FOUND.
In this case, the has_valid_directory_prefix is used to check if the parent directory exists.
So that, when the parent directory exists, mingw_lstat returns ENOTDIR.
ENOTDIR is not managed by the caller code (files_read_raw_ref in git/refs/files-backend.c).
It probably should.

Conclusion
This commit enables to take into account the case when ENOTDIR is returned by mingw_lstat.

Signed-off-by: pgrmega <pgarnier@mega.com>
dscho pushed a commit to pgrmega/git that referenced this issue Jul 14, 2022
Network shares sometimes use aggressive caching, in which case a
just-created directory might not be immediately visible to Git.

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 culprit is that `GetFileAttributesExW()` that is called from
`mingw_lstat()` can raise an error `ERROR_PATH_NOT_FOUND`, which is
translated to `ENOTDIR`, as opposed to `ENOENT` as expected on Linux.

Therefore, when trying to read a ref, let's allow `ENOTDIR` in addition
to `ENOENT` to indicate that said ref is missing.

This fixes git-for-windows#3727

Signed-off-by: Pierre Garnier <pgarnier@mega.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
dscho pushed a commit to pgrmega/git that referenced this issue Jul 14, 2022
Network shares sometimes use aggressive caching, in which case a
just-created directory might not be immediately visible to Git.

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 culprit is that `GetFileAttributesExW()` that is called from
`mingw_lstat()` can raise an error `ERROR_PATH_NOT_FOUND`, which is
translated to `ENOTDIR`, as opposed to `ENOENT` as expected on Linux.

Therefore, when trying to read a ref, let's allow `ENOTDIR` in addition
to `ENOENT` to indicate that said ref is missing.

This fixes git-for-windows#3727

Signed-off-by: Pierre Garnier <pgarnier@mega.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
dscho added a commit to dscho/git that referenced this issue Jul 15, 2022
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>
dscho added a commit to dscho/git that referenced this issue Jul 16, 2022
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>
dscho added a commit to dscho/git that referenced this issue Jul 28, 2022
Files' attributes can indicate more than just whether they are files or
directories. It was reported in Git for Windows that on certain network
shares, this let to a nasty problem trying to create tags:

	$ 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 with all types of network shares.
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/

In the indicated instance, the following commands worked around the bug:

	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 interpreted
as an enum value, not as a bit field, so that a perfectly fine leading
directory can be 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

Reported-by: Pierre Garnier <pgarnier@mega.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
dscho added a commit to dscho/git that referenced this issue Jul 28, 2022
Files' attributes can indicate more than just whether they are files or
directories. It was reported in Git for Windows that on certain network
shares, this let to a nasty problem trying to create tags:

	$ 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 with all types of network shares.
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/

in the indicated instance the following commands worked around the bug:

	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 interpreted
as an enum value, not as a bit field, so that a perfectly fine leading
directory can be 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

Reported-by: Pierre Garnier <pgarnier@mega.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
gitster pushed a commit to git/git that referenced this issue Jul 28, 2022
Files' attributes can indicate more than just whether they are files or
directories. It was reported in Git for Windows that on certain network
shares, this let to a nasty problem trying to create tags:

	$ 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 with all types of network shares.
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/

in the indicated instance the following commands worked around the bug:

	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 interpreted
as an enum value, not as a bit field, so that a perfectly fine leading
directory can be 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

Reported-by: Pierre Garnier <pgarnier@mega.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
gitster pushed a commit to git/git that referenced this issue Jul 29, 2022
Files' attributes can indicate more than just whether they are files or
directories. It was reported in Git for Windows that on certain network
shares, this led to a nasty problem trying to create tags:

	$ 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 with all types of network shares.
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/

in the indicated instance the following commands worked around the bug:

	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 interpreted
as an enum value, not as a bit field, so that a perfectly fine leading
directory can be 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

Reported-by: Pierre Garnier <pgarnier@mega.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
dscho added a commit to dscho/git that referenced this issue Jul 29, 2022
Files' attributes can indicate more than just whether they are files or
directories. It was reported in Git for Windows that on certain network
shares, this led to a nasty problem trying to create tags:

	$ 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 with all types of network shares.
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/

in the indicated instance the following commands worked around the bug:

	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 interpreted
as an enum value, not as a bit field, so that a perfectly fine leading
directory can be 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

Reported-by: Pierre Garnier <pgarnier@mega.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
@dscho dscho closed this as completed in b4f08ee Jul 29, 2022
dscho added a commit to git-for-windows/build-extra that referenced this issue Jul 29, 2022
With certain network drives, [it was
reported](git-for-windows/git#3727) that
some attributes associated with caching confused Git for Windows. This
[was fixed](git-for-windows/git#3753).

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
gitster pushed a commit to git/git that referenced this issue Jul 29, 2022
Files' attributes can indicate more than just whether they are files or
directories. It was reported in Git for Windows that on certain network
shares, this led to a nasty problem trying to create tags:

	$ 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 with all types of network shares.
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/

in the indicated instance the following commands worked around the bug:

	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 interpreted
as an enum value, not as a bit field, so that a perfectly fine leading
directory can be 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
`GetFileAttributesW()` call correctly.

This fixes git-for-windows#3727

Reported-by: Pierre Garnier <pgarnier@mega.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
git-for-windows-ci pushed a commit that referenced this issue Aug 12, 2022
Files' attributes can indicate more than just whether they are files or
directories. It was reported in Git for Windows that on certain network
shares, this led to a nasty problem trying to create tags:

	$ 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 with all types of network shares.
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/

in the indicated instance the following commands worked around the bug:

	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 interpreted
as an enum value, not as a bit field, so that a perfectly fine leading
directory can be 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
`GetFileAttributesW()` call correctly.

This fixes #3727

Reported-by: Pierre Garnier <pgarnier@mega.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
git-for-windows-ci pushed a commit that referenced this issue Aug 14, 2022
Files' attributes can indicate more than just whether they are files or
directories. It was reported in Git for Windows that on certain network
shares, this led to a nasty problem trying to create tags:

	$ 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 with all types of network shares.
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/

in the indicated instance the following commands worked around the bug:

	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 interpreted
as an enum value, not as a bit field, so that a perfectly fine leading
directory can be 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
`GetFileAttributesW()` call correctly.

This fixes #3727

Reported-by: Pierre Garnier <pgarnier@mega.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants