1010#include "win32/lazyload.h"
1111#include "../config.h"
1212#include "../string-list.h"
13+ #include "../attr.h"
1314
1415#define HCAST (type , handle ) ((type)(intptr_t)handle)
1516
@@ -401,6 +402,54 @@ static void process_phantom_symlinks(void)
401402 LeaveCriticalSection (& phantom_symlinks_cs );
402403}
403404
405+ static int create_phantom_symlink (wchar_t * wtarget , wchar_t * wlink )
406+ {
407+ int len ;
408+
409+ /* create file symlink */
410+ if (!CreateSymbolicLinkW (wlink , wtarget , symlink_file_flags )) {
411+ errno = err_win_to_posix (GetLastError ());
412+ return -1 ;
413+ }
414+
415+ /* convert to directory symlink if target exists */
416+ switch (process_phantom_symlink (wtarget , wlink )) {
417+ case PHANTOM_SYMLINK_RETRY : {
418+ /* if target doesn't exist, add to phantom symlinks list */
419+ wchar_t wfullpath [MAX_LONG_PATH ];
420+ struct phantom_symlink_info * psi ;
421+
422+ /* convert to absolute path to be independent of cwd */
423+ len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
424+ if (!len || len >= MAX_LONG_PATH ) {
425+ errno = err_win_to_posix (GetLastError ());
426+ return -1 ;
427+ }
428+
429+ /* over-allocate and fill phantom_symlink_info structure */
430+ psi = xmalloc (sizeof (struct phantom_symlink_info ) +
431+ sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
432+ psi -> wlink = (wchar_t * )(psi + 1 );
433+ wcscpy (psi -> wlink , wfullpath );
434+ psi -> wtarget = psi -> wlink + len + 1 ;
435+ wcscpy (psi -> wtarget , wtarget );
436+
437+ EnterCriticalSection (& phantom_symlinks_cs );
438+ psi -> next = phantom_symlinks ;
439+ phantom_symlinks = psi ;
440+ LeaveCriticalSection (& phantom_symlinks_cs );
441+ break ;
442+ }
443+ case PHANTOM_SYMLINK_DIRECTORY :
444+ /* if we created a dir symlink, process other phantom symlinks */
445+ process_phantom_symlinks ();
446+ break ;
447+ default :
448+ break ;
449+ }
450+ return 0 ;
451+ }
452+
404453/* Normalizes NT paths as returned by some low-level APIs. */
405454static wchar_t * normalize_ntpath (wchar_t * wbuf )
406455{
@@ -2819,6 +2868,35 @@ int link(const char *oldpath, const char *newpath)
28192868 return 0 ;
28202869}
28212870
2871+ enum symlink_type {
2872+ SYMLINK_TYPE_UNSPECIFIED = 0 ,
2873+ SYMLINK_TYPE_FILE ,
2874+ SYMLINK_TYPE_DIRECTORY ,
2875+ };
2876+
2877+ static enum symlink_type check_symlink_attr (const char * link )
2878+ {
2879+ static struct attr_check * check ;
2880+ const char * value ;
2881+ int r ;
2882+
2883+ if (!check )
2884+ check = attr_check_initl ("symlink" , NULL );
2885+
2886+ r = git_check_attr (& the_index , link , check );
2887+ assert (!r );
2888+
2889+ value = check -> items [0 ].value ;
2890+ if (value == NULL )
2891+ ;
2892+ else if (!strcmp (value , "file" ))
2893+ return SYMLINK_TYPE_FILE ;
2894+ else if (!strcmp (value , "dir" ))
2895+ return SYMLINK_TYPE_DIRECTORY ;
2896+
2897+ return SYMLINK_TYPE_UNSPECIFIED ;
2898+ }
2899+
28222900int symlink (const char * target , const char * link )
28232901{
28242902 wchar_t wtarget [MAX_LONG_PATH ], wlink [MAX_LONG_PATH ];
@@ -2839,48 +2917,31 @@ int symlink(const char *target, const char *link)
28392917 if (wtarget [len ] == '/' )
28402918 wtarget [len ] = '\\' ;
28412919
2842- /* create file symlink */
2843- if (!CreateSymbolicLinkW (wlink , wtarget , symlink_file_flags )) {
2844- errno = err_win_to_posix (GetLastError ());
2845- return -1 ;
2846- }
2847-
2848- /* convert to directory symlink if target exists */
2849- switch (process_phantom_symlink (wtarget , wlink )) {
2850- case PHANTOM_SYMLINK_RETRY : {
2851- /* if target doesn't exist, add to phantom symlinks list */
2852- wchar_t wfullpath [MAX_LONG_PATH ];
2853- struct phantom_symlink_info * psi ;
2854-
2855- /* convert to absolute path to be independent of cwd */
2856- len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
2857- if (!len || len >= MAX_LONG_PATH ) {
2858- errno = err_win_to_posix (GetLastError ());
2859- return -1 ;
2860- }
2861-
2862- /* over-allocate and fill phantom_symlink_info structure */
2863- psi = xmalloc (sizeof (struct phantom_symlink_info )
2864- + sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
2865- psi -> wlink = (wchar_t * )(psi + 1 );
2866- wcscpy (psi -> wlink , wfullpath );
2867- psi -> wtarget = psi -> wlink + len + 1 ;
2868- wcscpy (psi -> wtarget , wtarget );
2869-
2870- EnterCriticalSection (& phantom_symlinks_cs );
2871- psi -> next = phantom_symlinks ;
2872- phantom_symlinks = psi ;
2873- LeaveCriticalSection (& phantom_symlinks_cs );
2874- break ;
2875- }
2876- case PHANTOM_SYMLINK_DIRECTORY :
2877- /* if we created a dir symlink, process other phantom symlinks */
2920+ switch (check_symlink_attr (link )) {
2921+ case SYMLINK_TYPE_UNSPECIFIED :
2922+ /* Create a phantom symlink: it is initially created as a file
2923+ * symlink, but may change to a directory symlink later if/when
2924+ * the target exists. */
2925+ return create_phantom_symlink (wtarget , wlink );
2926+ case SYMLINK_TYPE_FILE :
2927+ if (!CreateSymbolicLinkW (wlink , wtarget , symlink_file_flags ))
2928+ break ;
2929+ return 0 ;
2930+ case SYMLINK_TYPE_DIRECTORY :
2931+ if (!CreateSymbolicLinkW (wlink , wtarget ,
2932+ symlink_directory_flags ))
2933+ break ;
2934+ /* There may be dangling phantom symlinks that point at this
2935+ * one, which should now morph into directory symlinks. */
28782936 process_phantom_symlinks ();
2879- break ;
2937+ return 0 ;
28802938 default :
2881- break ;
2939+ BUG ( "unhandled symlink type" ) ;
28822940 }
2883- return 0 ;
2941+
2942+ /* CreateSymbolicLinkW failed. */
2943+ errno = err_win_to_posix (GetLastError ());
2944+ return -1 ;
28842945}
28852946
28862947#ifndef _WINNT_H
0 commit comments