Message ID | pull.1291.v2.git.1659018558989.gitgitgadget@gmail.com (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | [v2] lstat(mingw): correctly detect ENOTDIR scenarios | expand |
"Johannes Schindelin via GitGitGadget" <gitgitgadget@gmail.com> writes: > 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". Nicely analyzed. So after all ENOTDIR was a buggy response and by fixing it we help all callers of lstat(), as pointed out in the earlier review. > * Thanks to Eric's excellent review, the reporter and I dug deeper and > figured out the real bug (and fix). Yeah, thanks all. Will queue.
On Thu, Jul 28, 2022 at 10:29 AM Johannes Schindelin via GitGitGadget <gitgitgadget@gmail.com> wrote: > 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: s/let/led/ (not worth a re-roll) > $ 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 4b0abd5c695 (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 https://github.com/git-for-windows/git/issues/3727 > > Reported-by: Pierre Garnier <pgarnier@mega.com> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
diff --git a/compat/mingw.c b/compat/mingw.c index 545e952a588..3b85bb02536 100644 --- a/compat/mingw.c +++ b/compat/mingw.c @@ -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()) {