88#include "../cache.h"
99#include "win32/lazyload.h"
1010#include "../config.h"
11+ #include "../attr.h"
1112
1213#define HCAST (type , handle ) ((type)(intptr_t)handle)
1314
@@ -399,6 +400,54 @@ static void process_phantom_symlinks(void)
399400 LeaveCriticalSection (& phantom_symlinks_cs );
400401}
401402
403+ static int create_phantom_symlink (wchar_t * wtarget , wchar_t * wlink )
404+ {
405+ int len ;
406+
407+ /* create file symlink */
408+ if (!CreateSymbolicLinkW (wlink , wtarget , symlink_file_flags )) {
409+ errno = err_win_to_posix (GetLastError ());
410+ return -1 ;
411+ }
412+
413+ /* convert to directory symlink if target exists */
414+ switch (process_phantom_symlink (wtarget , wlink )) {
415+ case PHANTOM_SYMLINK_RETRY : {
416+ /* if target doesn't exist, add to phantom symlinks list */
417+ wchar_t wfullpath [MAX_LONG_PATH ];
418+ struct phantom_symlink_info * psi ;
419+
420+ /* convert to absolute path to be independent of cwd */
421+ len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
422+ if (!len || len >= MAX_LONG_PATH ) {
423+ errno = err_win_to_posix (GetLastError ());
424+ return -1 ;
425+ }
426+
427+ /* over-allocate and fill phantom_symlink_info structure */
428+ psi = xmalloc (sizeof (struct phantom_symlink_info ) +
429+ sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
430+ psi -> wlink = (wchar_t * )(psi + 1 );
431+ wcscpy (psi -> wlink , wfullpath );
432+ psi -> wtarget = psi -> wlink + len + 1 ;
433+ wcscpy (psi -> wtarget , wtarget );
434+
435+ EnterCriticalSection (& phantom_symlinks_cs );
436+ psi -> next = phantom_symlinks ;
437+ phantom_symlinks = psi ;
438+ LeaveCriticalSection (& phantom_symlinks_cs );
439+ break ;
440+ }
441+ case PHANTOM_SYMLINK_DIRECTORY :
442+ /* if we created a dir symlink, process other phantom symlinks */
443+ process_phantom_symlinks ();
444+ break ;
445+ default :
446+ break ;
447+ }
448+ return 0 ;
449+ }
450+
402451/* Normalizes NT paths as returned by some low-level APIs. */
403452static wchar_t * normalize_ntpath (wchar_t * wbuf )
404453{
@@ -2478,6 +2527,33 @@ int link(const char *oldpath, const char *newpath)
24782527 return 0 ;
24792528}
24802529
2530+ enum symlink_type {
2531+ SYMLINK_TYPE_UNSPECIFIED = 0 ,
2532+ SYMLINK_TYPE_FILE ,
2533+ SYMLINK_TYPE_DIRECTORY ,
2534+ };
2535+
2536+ static enum symlink_type check_symlink_attr (const char * link )
2537+ {
2538+ static struct attr_check * check ;
2539+ const char * value ;
2540+
2541+ if (!check )
2542+ check = attr_check_initl ("symlink" , NULL );
2543+
2544+ git_check_attr (& the_index , link , check );
2545+
2546+ value = check -> items [0 ].value ;
2547+ if (value == NULL )
2548+ ;
2549+ else if (!strcmp (value , "file" ))
2550+ return SYMLINK_TYPE_FILE ;
2551+ else if (!strcmp (value , "dir" ))
2552+ return SYMLINK_TYPE_DIRECTORY ;
2553+
2554+ return SYMLINK_TYPE_UNSPECIFIED ;
2555+ }
2556+
24812557int symlink (const char * target , const char * link )
24822558{
24832559 wchar_t wtarget [MAX_LONG_PATH ], wlink [MAX_LONG_PATH ];
@@ -2498,48 +2574,31 @@ int symlink(const char *target, const char *link)
24982574 if (wtarget [len ] == '/' )
24992575 wtarget [len ] = '\\' ;
25002576
2501- /* create file symlink */
2502- if (!CreateSymbolicLinkW (wlink , wtarget , symlink_file_flags )) {
2503- errno = err_win_to_posix (GetLastError ());
2504- return -1 ;
2505- }
2506-
2507- /* convert to directory symlink if target exists */
2508- switch (process_phantom_symlink (wtarget , wlink )) {
2509- case PHANTOM_SYMLINK_RETRY : {
2510- /* if target doesn't exist, add to phantom symlinks list */
2511- wchar_t wfullpath [MAX_LONG_PATH ];
2512- struct phantom_symlink_info * psi ;
2513-
2514- /* convert to absolute path to be independent of cwd */
2515- len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
2516- if (!len || len >= MAX_LONG_PATH ) {
2517- errno = err_win_to_posix (GetLastError ());
2518- return -1 ;
2519- }
2520-
2521- /* over-allocate and fill phantom_symlink_info structure */
2522- psi = xmalloc (sizeof (struct phantom_symlink_info )
2523- + sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
2524- psi -> wlink = (wchar_t * )(psi + 1 );
2525- wcscpy (psi -> wlink , wfullpath );
2526- psi -> wtarget = psi -> wlink + len + 1 ;
2527- wcscpy (psi -> wtarget , wtarget );
2528-
2529- EnterCriticalSection (& phantom_symlinks_cs );
2530- psi -> next = phantom_symlinks ;
2531- phantom_symlinks = psi ;
2532- LeaveCriticalSection (& phantom_symlinks_cs );
2533- break ;
2534- }
2535- case PHANTOM_SYMLINK_DIRECTORY :
2536- /* if we created a dir symlink, process other phantom symlinks */
2577+ switch (check_symlink_attr (link )) {
2578+ case SYMLINK_TYPE_UNSPECIFIED :
2579+ /* Create a phantom symlink: it is initially created as a file
2580+ * symlink, but may change to a directory symlink later if/when
2581+ * the target exists. */
2582+ return create_phantom_symlink (wtarget , wlink );
2583+ case SYMLINK_TYPE_FILE :
2584+ if (!CreateSymbolicLinkW (wlink , wtarget , symlink_file_flags ))
2585+ break ;
2586+ return 0 ;
2587+ case SYMLINK_TYPE_DIRECTORY :
2588+ if (!CreateSymbolicLinkW (wlink , wtarget ,
2589+ symlink_directory_flags ))
2590+ break ;
2591+ /* There may be dangling phantom symlinks that point at this
2592+ * one, which should now morph into directory symlinks. */
25372593 process_phantom_symlinks ();
2538- break ;
2594+ return 0 ;
25392595 default :
2540- break ;
2596+ BUG ( "unhandled symlink type" ) ;
25412597 }
2542- return 0 ;
2598+
2599+ /* CreateSymbolicLinkW failed. */
2600+ errno = err_win_to_posix (GetLastError ());
2601+ return -1 ;
25432602}
25442603
25452604#ifndef _WINNT_H
0 commit comments