@@ -319,6 +319,131 @@ int mingw_core_config(const char *var, const char *value, void *cb)
319319 return 0 ;
320320}
321321
322+ enum phantom_symlink_result {
323+ PHANTOM_SYMLINK_RETRY ,
324+ PHANTOM_SYMLINK_DONE ,
325+ PHANTOM_SYMLINK_DIRECTORY
326+ };
327+
328+ static inline int is_wdir_sep (wchar_t wchar )
329+ {
330+ return wchar == L'/' || wchar == L'\\' ;
331+ }
332+
333+ static const wchar_t * make_relative_to (const wchar_t * path ,
334+ const wchar_t * relative_to , wchar_t * out ,
335+ size_t size )
336+ {
337+ size_t i = wcslen (relative_to ), len ;
338+
339+ /* Is `path` already absolute? */
340+ if (is_wdir_sep (path [0 ]) ||
341+ (iswalpha (path [0 ]) && path [1 ] == L':' && is_wdir_sep (path [2 ])))
342+ return path ;
343+
344+ while (i > 0 && !is_wdir_sep (relative_to [i - 1 ]))
345+ i -- ;
346+
347+ /* Is `relative_to` in the current directory? */
348+ if (!i )
349+ return path ;
350+
351+ len = wcslen (path );
352+ if (i + len + 1 > size ) {
353+ error ("Could not make '%ls' relative to '%ls' (too large)" ,
354+ path , relative_to );
355+ return NULL ;
356+ }
357+
358+ memcpy (out , relative_to , i * sizeof (wchar_t ));
359+ wcscpy (out + i , path );
360+ return out ;
361+ }
362+
363+ /*
364+ * Changes a file symlink to a directory symlink if the target exists and is a
365+ * directory.
366+ */
367+ static enum phantom_symlink_result
368+ process_phantom_symlink (const wchar_t * wtarget , const wchar_t * wlink )
369+ {
370+ HANDLE hnd ;
371+ BY_HANDLE_FILE_INFORMATION fdata ;
372+ wchar_t relative [MAX_LONG_PATH ];
373+ const wchar_t * rel ;
374+
375+ /* check that wlink is still a file symlink */
376+ if ((GetFileAttributesW (wlink )
377+ & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY ))
378+ != FILE_ATTRIBUTE_REPARSE_POINT )
379+ return PHANTOM_SYMLINK_DONE ;
380+
381+ /* make it relative, if necessary */
382+ rel = make_relative_to (wtarget , wlink , relative , ARRAY_SIZE (relative ));
383+ if (!rel )
384+ return PHANTOM_SYMLINK_DONE ;
385+
386+ /* let Windows resolve the link by opening it */
387+ hnd = CreateFileW (rel , 0 ,
388+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE , NULL ,
389+ OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
390+ if (hnd == INVALID_HANDLE_VALUE ) {
391+ errno = err_win_to_posix (GetLastError ());
392+ return PHANTOM_SYMLINK_RETRY ;
393+ }
394+
395+ if (!GetFileInformationByHandle (hnd , & fdata )) {
396+ errno = err_win_to_posix (GetLastError ());
397+ CloseHandle (hnd );
398+ return PHANTOM_SYMLINK_RETRY ;
399+ }
400+ CloseHandle (hnd );
401+
402+ /* if target exists and is a file, we're done */
403+ if (!(fdata .dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ))
404+ return PHANTOM_SYMLINK_DONE ;
405+
406+ /* otherwise recreate the symlink with directory flag */
407+ if (DeleteFileW (wlink ) && CreateSymbolicLinkW (wlink , wtarget , 1 ))
408+ return PHANTOM_SYMLINK_DIRECTORY ;
409+
410+ errno = err_win_to_posix (GetLastError ());
411+ return PHANTOM_SYMLINK_RETRY ;
412+ }
413+
414+ /* keep track of newly created symlinks to non-existing targets */
415+ struct phantom_symlink_info {
416+ struct phantom_symlink_info * next ;
417+ wchar_t * wlink ;
418+ wchar_t * wtarget ;
419+ };
420+
421+ static struct phantom_symlink_info * phantom_symlinks = NULL ;
422+ static CRITICAL_SECTION phantom_symlinks_cs ;
423+
424+ static void process_phantom_symlinks (void )
425+ {
426+ struct phantom_symlink_info * current , * * psi ;
427+ EnterCriticalSection (& phantom_symlinks_cs );
428+ /* process phantom symlinks list */
429+ psi = & phantom_symlinks ;
430+ while ((current = * psi )) {
431+ enum phantom_symlink_result result = process_phantom_symlink (
432+ current -> wtarget , current -> wlink );
433+ if (result == PHANTOM_SYMLINK_RETRY ) {
434+ psi = & current -> next ;
435+ } else {
436+ /* symlink was processed, remove from list */
437+ * psi = current -> next ;
438+ free (current );
439+ /* if symlink was a directory, start over */
440+ if (result == PHANTOM_SYMLINK_DIRECTORY )
441+ psi = & phantom_symlinks ;
442+ }
443+ }
444+ LeaveCriticalSection (& phantom_symlinks_cs );
445+ }
446+
322447/* Normalizes NT paths as returned by some low-level APIs. */
323448static wchar_t * normalize_ntpath (wchar_t * wbuf )
324449{
@@ -502,6 +627,8 @@ int mingw_mkdir(const char *path, int mode)
502627 return -1 ;
503628
504629 ret = _wmkdir (wpath );
630+ if (!ret )
631+ process_phantom_symlinks ();
505632 if (!ret && needs_hiding (path ))
506633 return set_hidden_flag (wpath , 1 );
507634 return ret ;
@@ -2737,6 +2864,42 @@ int symlink(const char *target, const char *link)
27372864 errno = err_win_to_posix (GetLastError ());
27382865 return -1 ;
27392866 }
2867+
2868+ /* convert to directory symlink if target exists */
2869+ switch (process_phantom_symlink (wtarget , wlink )) {
2870+ case PHANTOM_SYMLINK_RETRY : {
2871+ /* if target doesn't exist, add to phantom symlinks list */
2872+ wchar_t wfullpath [MAX_LONG_PATH ];
2873+ struct phantom_symlink_info * psi ;
2874+
2875+ /* convert to absolute path to be independent of cwd */
2876+ len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
2877+ if (!len || len >= MAX_LONG_PATH ) {
2878+ errno = err_win_to_posix (GetLastError ());
2879+ return -1 ;
2880+ }
2881+
2882+ /* over-allocate and fill phantom_symlink_info structure */
2883+ psi = xmalloc (sizeof (struct phantom_symlink_info )
2884+ + sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
2885+ psi -> wlink = (wchar_t * )(psi + 1 );
2886+ wcscpy (psi -> wlink , wfullpath );
2887+ psi -> wtarget = psi -> wlink + len + 1 ;
2888+ wcscpy (psi -> wtarget , wtarget );
2889+
2890+ EnterCriticalSection (& phantom_symlinks_cs );
2891+ psi -> next = phantom_symlinks ;
2892+ phantom_symlinks = psi ;
2893+ LeaveCriticalSection (& phantom_symlinks_cs );
2894+ break ;
2895+ }
2896+ case PHANTOM_SYMLINK_DIRECTORY :
2897+ /* if we created a dir symlink, process other phantom symlinks */
2898+ process_phantom_symlinks ();
2899+ break ;
2900+ default :
2901+ break ;
2902+ }
27402903 return 0 ;
27412904}
27422905
@@ -3648,6 +3811,7 @@ int wmain(int argc, const wchar_t **wargv)
36483811
36493812 /* initialize critical section for waitpid pinfo_t list */
36503813 InitializeCriticalSection (& pinfo_cs );
3814+ InitializeCriticalSection (& phantom_symlinks_cs );
36513815
36523816 /* initialize critical section for fscache */
36533817 InitializeCriticalSection (& fscache_cs );
0 commit comments