mingw: implement basic `symlink()` functionality (file symlinks only)

Implement `symlink()`. This implementation always creates _file_ symlinks (remember: Windows discerns between symlinks pointing to directories and those pointing to files). Support for directory symlinks will be added in a subseqeuent commit. This implementation fails with `ENOSYS` if symlinks are disabled or unsupported. Signed-off-by: Karsten Blees <karsten.blees@gmail.com> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Karsten Blees committed Jan 9, 2026 at 20:05 UTC 593008b95dfd4898f182d664d6b162c7590b4028
2 files changed +29 -2
compat/mingw-posix.h
+1 -2
@@ -121,8 +121,6 @@ struct utsname {
121 * trivial stubs
122 */
123
124 -static inline int symlink(const char *oldpath UNUSED, const char *newpath UNUSED)
125 -{ errno = ENOSYS; return -1; }
124 static inline int fchmod(int fildes UNUSED, mode_t mode UNUSED)
125 { errno = ENOSYS; return -1; }
126 #ifndef __MINGW64_VERSION_MAJOR
@@ -195,6 +193,7 @@ int setitimer(int type, struct itimerval *in, struct itimerval *out);
193 int sigaction(int sig, struct sigaction *in, struct sigaction *out);
194 int link(const char *oldpath, const char *newpath);
195 int uname(struct utsname *buf);
196 +int symlink(const char *target, const char *link);
197 int readlink(const char *path, char *buf, size_t bufsiz);
198
199 /*
compat/mingw.c
+28
@@ -2698,6 +2698,34 @@ int link(const char *oldpath, const char *newpath)
2698 return 0;
2699 }
2700
2701 +int symlink(const char *target, const char *link)
2702 +{
2703 + wchar_t wtarget[MAX_PATH], wlink[MAX_PATH];
2704 + int len;
2705 +
2706 + /* fail if symlinks are disabled or API is not supported (WinXP) */
2707 + if (!has_symlinks) {
2708 + errno = ENOSYS;
2709 + return -1;
2710 + }
2711 +
2712 + if ((len = xutftowcs_path(wtarget, target)) < 0
2713 + || xutftowcs_path(wlink, link) < 0)
2714 + return -1;
2715 +
2716 + /* convert target dir separators to backslashes */
2717 + while (len--)
2718 + if (wtarget[len] == '/')
2719 + wtarget[len] = '\\';
2720 +
2721 + /* create file symlink */
2722 + if (!CreateSymbolicLinkW(wlink, wtarget, 0)) {
2723 + errno = err_win_to_posix(GetLastError());
2724 + return -1;
2725 + }
2726 + return 0;
2727 +}
2728 +
2729 int readlink(const char *path, char *buf, size_t bufsiz)
2730 {
2731 WCHAR wpath[MAX_PATH];