Skip to content

Support symbolic links on Windows

Johannes Schindelin via GitGitGadget git 2 versions – New

Get series with b4
b4 am [email protected]

Versions newest first · linked by author and title

  1. v2 Latest18 patches 0 repliesNew
  2. v118 patches 13 repliesSuperseded

Changes in v2 from the cover letter

* Changed Karsten's email address to the current one.
* The changes to do_lstat() are now reflected by the comment preceding that function.
* The commit message mentioning some ELOOP logic when symlink support is disabled, which had no corresponding part in the commit's diff, was adjusted to no longer mention that long gone change.
* Fixed the typo "woutl".
* Dropped a misleading, stale comment about ENOSYS in the readlink() implementation (talking about a long-dropped part of the original patch).

Patches in v2 No applies recorded yet

#Subject and filesSizeRepliesApplied
1/18mingw: don't call `GetFileAttributes()` twice in `mingw_lstat()`compat/mingw.c+15 -380 replies
2/18mingw: implement `stat()` with symlink supportcompat/mingw.c+18 -10 replies
3/18mingw: drop the separate `do_lstat()` functioncompat/mingw.c+2 -210 replies
4/18mingw: let `mingw_lstat()` error early upon problems with reparse pointscompat/mingw.c+14 -100 replies
5/18mingw: teach dirent about symlinkscompat/mingw.c compat/win32.h compat/win32/dirent.c+11 -130 replies
6/18mingw: compute the correct size for symlinks in `mingw_lstat()`compat/mingw.c+104 -100 replies
7/18mingw: factor out the retry logiccompat/mingw.c+46 -580 replies
8/18mingw: change default of `core.symlinks` to falsecompat/mingw.c+9 -00 replies
9/18mingw: add symlink-specific error codescompat/mingw.c+5 -00 replies
10/18mingw: handle symlinks to directories in `mingw_unlink()`compat/mingw.c+8 -10 replies
11/18mingw: support renaming symlinkscompat/mingw.c+16 -220 replies
12/18mingw: allow `mingw_chdir()` to change to symlink-resolved directoriescompat/mingw.c+19 -10 replies
13/18mingw: implement `readlink()`compat/mingw-posix.h compat/mingw.c+25 -20 replies
14/18mingw: implement basic `symlink()` functionality (file symlinks only)compat/mingw-posix.h compat/mingw.c+29 -20 replies
15/18mingw: add support for symlinks to directoriescompat/mingw.c+164 -00 replies
16/18mingw: try to create symlinks without elevated permissionscompat/mingw.c+24 -20 replies
17/18mingw: emulate `stat()` a little more faithfullycompat/mingw.c+13 -10 replies
18/18mingw: special-case index entries for symlinks with buggy sizeread-cache.c+11 -00 replies

Reviews trailers per patch

No Reviewed-by, Acked-by or Tested-by trailers for v2 yet.

Read the v2 thread

Cover letter [PATCH v2 00/18] Support symbolic links on Windows

This finally upstreams Git for Windows' support for Windows' branch of symbolic links, which has been maturing since 2015. It is based off of js/prep-symlink-windows.

Changes since v1:

* Changed Karsten's email address to the current one.
* The changes to do_lstat() are now reflected by the comment preceding that function.
* The commit message mentioning some ELOOP logic when symlink support is disabled, which had no corresponding part in the commit's diff, was adjusted to no longer mention that long gone change.
* Fixed the typo "woutl".
* Dropped a misleading, stale comment about ENOSYS in the readlink() implementation (talking about a long-dropped part of the original patch).

Bill Zissimopoulos (1):
mingw: compute the correct size for symlinks in mingw_lstat()

Johannes Schindelin (3):
mingw: try to create symlinks without elevated permissions
mingw: emulate stat() a little more faithfully
mingw: special-case index entries for symlinks with buggy size

Karsten Blees (14):
mingw: don't call GetFileAttributes() twice in mingw_lstat()
mingw: implement stat() with symlink support
mingw: drop the separate do_lstat() function
mingw: let mingw_lstat() error early upon problems with reparse
points
mingw: teach dirent about symlinks
mingw: factor out the retry logic
mingw: change default of core.symlinks to false
mingw: add symlink-specific error codes
mingw: handle symlinks to directories in mingw_unlink()
mingw: support renaming symlinks
mingw: allow mingw_chdir() to change to symlink-resolved directories
mingw: implement readlink()
mingw: implement basic symlink() functionality (file symlinks only)
mingw: add support for symlinks to directories

 compat/mingw-posix.h  |   6 +-
 compat/mingw.c        | 635 ++++++++++++++++++++++++++++++++----------
 compat/win32.h        |   6 +-
 compat/win32/dirent.c |   5 +-
 read-cache.c          |  11 +
 5 files changed, 507 insertions(+), 156 deletions(-)

base-commit: 1887b3dd06823575e37ad19b5827d467e126c6ed
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2018%2Fdscho%2Fsymlinks-next-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2018/dscho/symlinks-next-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/2018

Range-diff vs v1:

1: dae450dd0e ! 1: 6ec4ff7457 mingw: don't call GetFileAttributes() twice in mingw_lstat()
@@
## Metadata ##
-Author: Karsten Blees <[email protected]>
+Author: Karsten Blees <[email protected]>

## Commit message ##
mingw: don't call GetFileAttributes() twice in mingw_lstat()
@@ Commit message
free, and also have a (wide char) buffer that can be modified. This makes it easy to avoid that extraneous Win32 API call.

- Signed-off-by: Karsten Blees <[email protected]>
+ Signed-off-by: Karsten Blees <[email protected]>

## compat/mingw.c ##
+@@ compat/mingw.c: static int has_valid_directory_prefix(wchar_t *wfilename)
+ }
+
+ /* We keep the do_lstat code in a separate function to avoid recursion.
+- * When a path ends with a slash, the stat will fail with ENOENT. In
+- * this case, we strip the trailing slashes and stat again.
++ * When a path ends with a slash, the call to GetFileAttributedExW()
++ * would fail. To prevent this, we strip any trailing slashes before that ++ * call.
+ *
+ * If follow is true then act like stat() and report on the link
+ * target. Otherwise report on the link itself.
@@ compat/mingw.c: static int do_lstat(int follow, const char *file_name, struct stat *buf)
{
WIN32_FILE_ATTRIBUTE_DATA fdata;
2: c36848eda7 ! 2: 5d83a8ab76 mingw: implement stat() with symlink support
@@
## Metadata ##
-Author: Karsten Blees <[email protected]>
+Author: Karsten Blees <[email protected]>

## Commit message ##
mingw: implement stat() with symlink support
@@ Commit message
permissions, and then calling GetFileInformationByHandle() on it. This way, all links are resolved by the Windows file system layer.

- If symlinks are disabled, use mingw_lstat() as before, but fail with
- ELOOP if a symlink would have to be resolved.
-
- Signed-off-by: Karsten Blees <[email protected]>
+ Signed-off-by: Karsten Blees <[email protected]>

## compat/mingw.c ##
3: aa0ca80bbb ! 3: d3953f1826 mingw: drop the separate do_lstat() function
@@
## Metadata ##
-Author: Karsten Blees <[email protected]>
+Author: Karsten Blees <[email protected]>

## Commit message ##
mingw: drop the separate do_lstat() function
@@ Commit message
the extra function and the old mingw_stat()-specific (follow == 1) logic.

- Signed-off-by: Karsten Blees <[email protected]>
+ Signed-off-by: Karsten Blees <[email protected]>

## compat/mingw.c ##
@@ compat/mingw.c: static int has_valid_directory_prefix(wchar_t *wfilename)
}

-/* We keep the do_lstat code in a separate function to avoid recursion.
-- * When a path ends with a slash, the stat will fail with ENOENT. In
-- * this case, we strip the trailing slashes and stat again.
+- * When a path ends with a slash, the call to GetFileAttributedExW()
+- * would fail. To prevent this, we strip any trailing slashes before that
+- * call.
- *
- * If follow is true then act like stat() and report on the link
- * target. Otherwise report on the link itself.
4: 886044373b ! 4: a6355789c3 mingw: let mingw_lstat() error early upon problems with reparse points
@@
## Metadata ##
-Author: Karsten Blees <[email protected]>
+Author: Karsten Blees <[email protected]>

## Commit message ##
mingw: let mingw_lstat() error early upon problems with reparse points
@@ Commit message
--color-moved -w. That code was _not_ moved because a subsequent commit will move it to an altogether different function, anyway.

- Signed-off-by: Karsten Blees <[email protected]>
+ Signed-off-by: Karsten Blees <[email protected]>

## compat/mingw.c ##
5: db1d156aa0 ! 5: 8b7f5a8fc7 mingw: teach dirent about symlinks
@@
## Metadata ##
-Author: Karsten Blees <[email protected]>
+Author: Karsten Blees <[email protected]>

## Commit message ##
mingw: teach dirent about symlinks
@@ Commit message

Implement DT_LNK detection in dirent.c's readdir() function.

- Signed-off-by: Karsten Blees <[email protected]>
+ Signed-off-by: Karsten Blees <[email protected]>

## compat/mingw.c ##

  6:  4c49a3d9bf =  6:  1159e86dc4 mingw: compute the correct size for symlinks in `mingw_lstat()`
  7:  ad74d540f2 !  7:  4aeccd6656 mingw: factor out the retry logic

@@
## Metadata ##
-Author: Karsten Blees <[email protected]> +Author: Karsten Blees <[email protected]>

## Commit message ##
mingw: factor out the retry logic
@@ Commit message

In mingw_rmdir(), we include special error handling in the retry loop.

- Signed-off-by: Karsten Blees <[email protected]>
+ Signed-off-by: Karsten Blees <[email protected]>

## compat/mingw.c ##
8: 25313cea76 ! 8: 86c0742748 mingw: change default of core.symlinks to false
@@
## Metadata ##
-Author: Karsten Blees <[email protected]>
+Author: Karsten Blees <[email protected]>

## Commit message ##
mingw: change default of core.symlinks to false
@@ Commit message
is true for the time being (an experiment to switch to BusyBox-w32 failed due to the experimental nature of BusyBox-w32).

- Signed-off-by: Karsten Blees <[email protected]>
+ Signed-off-by: Karsten Blees <[email protected]>

## compat/mingw.c ##
9: b698f4a002 ! 9: 9a0093d34b mingw: add symlink-specific error codes
@@
## Metadata ##
-Author: Karsten Blees <[email protected]>
+Author: Karsten Blees <[email protected]>

## Commit message ##
mingw: add symlink-specific error codes
@@ Commit message
Let's handle a couple of symlink-related error codes that will become relevant with the upcoming support for symlinks on Windows.

- Signed-off-by: Karsten Blees <[email protected]>
+ Signed-off-by: Karsten Blees <[email protected]>

## compat/mingw.c ##
10: 282aba42e8 ! 10: 5dc90f9785 mingw: handle symlinks to directories in mingw_unlink()
@@
## Metadata ##
-Author: Karsten Blees <[email protected]>
+Author: Karsten Blees <[email protected]>

## Commit message ##
mingw: handle symlinks to directories in mingw_unlink()

The _wunlink() and DeleteFileW() functions refuse to delete symlinks
- to directories on Windows; The error code woutl be ERROR_ACCESS_DENIED
+ to directories on Windows; The error code would be ERROR_ACCESS_DENIED
in that case. Take that error code as an indicator that we need to try _wrmdir() as well. In the best case, it will remove a symlink. In the worst case, it will fail with the same error code again.

- Signed-off-by: Karsten Blees <[email protected]>
+ Signed-off-by: Karsten Blees <[email protected]>

## compat/mingw.c ##
11: 5cb3b10500 ! 11: 3670a0a181 mingw: support renaming symlinks
@@
## Metadata ##
-Author: Karsten Blees <[email protected]>
+Author: Karsten Blees <[email protected]>

## Commit message ##
mingw: support renaming symlinks
@@ Commit message
Avoid the _wrename() call, and go with directly calling MoveFileEx(), with proper error handling of course.

- Signed-off-by: Karsten Blees <[email protected]>
+ Signed-off-by: Karsten Blees <[email protected]>

## compat/mingw.c ##
12: 49920839bb ! 12: f79cf31dab mingw: allow mingw_chdir() to change to symlink-resolved directories
@@
## Metadata ##
-Author: Karsten Blees <[email protected]>
+Author: Karsten Blees <[email protected]>

## Commit message ##
mingw: allow mingw_chdir() to change to symlink-resolved directories
@@ Commit message
Windows is limited to only MAX_PATH (260) characters. Therefore using symlinks and long paths in combination may be problematic.

- Signed-off-by: Karsten Blees <[email protected]>
+ Signed-off-by: Karsten Blees <[email protected]>

## compat/mingw.c ##
13: 8fef8220f4 ! 13: 1ae9c63a89 mingw: implement readlink()
@@
## Metadata ##
-Author: Karsten Blees <[email protected]>
+Author: Karsten Blees <[email protected]>

## Commit message ##
mingw: implement readlink()
@@ Commit message
Implement readlink() by reading NTFS reparse points via the read_reparse_point() function that was introduced earlier to determine the length of symlink targets. Works for symlinks and directory
- junctions. If symlinks are disabled, fail with ENOSYS.
+ junctions.

- Signed-off-by: Karsten Blees <[email protected]>
+ Signed-off-by: Karsten Blees <[email protected]>

## compat/mingw-posix.h ##
14: 1dd5f9d6cd ! 14: 4e0ac43ef6 mingw: implement basic symlink() functionality (file symlinks only)
@@
## Metadata ##
-Author: Karsten Blees <[email protected]>
+Author: Karsten Blees <[email protected]>

## Commit message ##
mingw: implement basic symlink() functionality (file symlinks only)
@@ Commit message
This implementation fails with ENOSYS if symlinks are disabled or unsupported.

- Signed-off-by: Karsten Blees <[email protected]>
+ Signed-off-by: Karsten Blees <[email protected]>

## compat/mingw-posix.h ##
15: 7b6dbc73f7 ! 15: 3d479fd47e mingw: add support for symlinks to directories
@@
## Metadata ##
-Author: Karsten Blees <[email protected]>
+Author: Karsten Blees <[email protected]>

## Commit message ##
mingw: add support for symlinks to directories
@@ Commit message
to directory or vice versa, or if the target directory is created in another process. It's the best Git can do, though.

- Signed-off-by: Karsten Blees <[email protected]>
+ Signed-off-by: Karsten Blees <[email protected]>

## compat/mingw.c ##

 16:  d3b89c29b0 = 16:  fb6aa461da mingw: try to create symlinks without elevated permissions
 17:  2e73ab4221 = 17:  40c3f7f36e mingw: emulate `stat()` a little more faithfully
 18:  817f488523 = 18:  afcf2bbfcb mingw: special-case index entries for symlinks with buggy size
Signature · 1 line
gitgitgadget