Skip to content

Commit e78ce02

Browse files
EliahKaganGit for Windows Build Agent
authored and
Git for Windows Build Agent
committed
compat/mingw: rename the symlink, not the target
Since 183ea3e [1], 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 b30404d [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]: 183ea3e [2]: b30404d [3]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew [4]: #5436 Signed-off-by: Eliah Kagan <[email protected]>
1 parent c016306 commit e78ce02

File tree

1 file changed

+3
-1
lines changed

1 file changed

+3
-1
lines changed

compat/mingw.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -2926,7 +2926,9 @@ int mingw_rename(const char *pold, const char *pnew)
29262926

29272927
old_handle = CreateFileW(wpold, DELETE,
29282928
FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
2929-
NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
2929+
NULL, OPEN_EXISTING,
2930+
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT,
2931+
NULL);
29302932
if (old_handle == INVALID_HANDLE_VALUE) {
29312933
errno = err_win_to_posix(GetLastError());
29322934
return -1;

0 commit comments

Comments
 (0)