git/compat
Eliah Kagan 975fc0471a compat/mingw: rename the symlink, not the target
Since 183ea3ea (Merge branch 'ps/mingw-rename', 2024-11-13),
a new technique is used on Windows to rename files, where supported.
The first step of this technique is to open the file with
`CreateFileW`. At that time, `FILE_ATTRIBUTE_NORMAL` was passed as
the value of the `dwFlagsAndAttributes` argument. In b30404df [2], this
was improved by passing `FILE_FLAG_BACKUP_SEMANTICS`, to support
directories as well as regular files.

However, neither value of `dwFlagsAndAttributes` is sufficient to open
a symbolic link with the correct semantics to rename it. Symlinks on
Windows are reparse points. Attempting to open a reparse point with
`CreateFileW` dereferences the reparse point and opens the target
instead, unless `FILE_FLAG_OPEN_REPARSE_POINT` is included in
`dwFlagsAndAttributes`. This is documented for that flag and in the
"Symbolic Link Behavior" section of the `CreateFileW` docs [3].

This produces a regression where attempting to rename a symlink on
Windows renames its target to the intended new name and location of the
symlink. For example, if `symlink` points to `file`, then running

    git mv symlink symlink-renamed

leaves `symlink` in place and unchanged, but renames `file` to
`symlink-renamed` [4].

This regression is detectable by existing tests in `t7001-mv.sh`, but
the tests must be run by a Windows user with the ability to create
symlinks, and the `ln -s` command used to create the initial symlink
must also be able to create a real symlink (such as by setting the
`MSYS` environment variable to `winsymlinks:nativestrict`). Then
these two tests fail if the regression is present, and pass otherwise:

    38 - git mv should overwrite file with a symlink
    39 - check moved symlink

Let's fix this, so that renaming a symlink again renames the symlink
itself and leaves the target unchanged, by passing

    FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT

as the `dwFlagsAndAttributes` argument. This is sufficient (and safe)
because including `FILE_FLAG_OPEN_REPARSE_POINT` causes no harm even
when used to open a file or directory that is not a reparse point. In
that case, as noted in [3], this flag is simply ignored.

[1]: 183ea3eabf
[2]: b30404dfc0
[3]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
[4]: https://github.com/git-for-windows/git/issues/5436

Signed-off-by: Eliah Kagan <eliah.kagan@gmail.com>
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2025-02-21 10:24:43 -08:00
..
fsmonitor global: trivial conversions to fix `-Wsign-compare` warnings 2024-12-06 20:20:04 +09:00
linux
nedmalloc
poll global: mark code units that generate warnings with `-Wsign-compare` 2024-12-06 20:20:02 +09:00
regex compat/regex: explicitly ignore "-Wsign-compare" warnings 2024-12-06 20:20:01 +09:00
simple-ipc
stub
vcbuild
win32 compat/win32: fix -Wsign-compare warning in "wWinMain()" 2024-12-06 20:20:01 +09:00
.gitattributes
access.c
apple-common-crypto.h
basename.c
bswap.h bswap.h: squelch potential sparse -Wcast-truncate warnings 2025-01-21 08:42:55 -08:00
compiler.h
disk.h
fileno.c
fopen.c
hstrerror.c
inet_ntop.c
inet_pton.c
memmem.c
mingw.c compat/mingw: rename the symlink, not the target 2025-02-21 10:24:43 -08:00
mingw.h
mkdir.c
mkdtemp.c
mmap.c
msvc.c
msvc.h
nonblock.c
nonblock.h
obstack.c
obstack.h
open.c
pread.c
precompose_utf8.c
precompose_utf8.h
qsort_s.c
regcomp_enhanced.c
setenv.c
sha1-chunked.c
sha1-chunked.h
snprintf.c
stat.c
strcasestr.c
strdup.c
strlcpy.c
strtoimax.c
strtoumax.c
terminal.c global: trivial conversions to fix `-Wsign-compare` warnings 2024-12-06 20:20:04 +09:00
terminal.h
unsetenv.c
win32.h
win32mmap.c global: mark code units that generate warnings with `-Wsign-compare` 2024-12-06 20:20:02 +09:00
winansi.c global: mark code units that generate warnings with `-Wsign-compare` 2024-12-06 20:20:02 +09:00
zlib-compat.h compat/zlib: allow use of zlib-ng as backend 2025-01-28 13:03:23 -08:00