@@ -330,6 +330,131 @@ int mingw_core_config(const char *var, const char *value,
330330 return 0 ;
331331}
332332
333+ enum phantom_symlink_result {
334+ PHANTOM_SYMLINK_RETRY ,
335+ PHANTOM_SYMLINK_DONE ,
336+ PHANTOM_SYMLINK_DIRECTORY
337+ };
338+
339+ static inline int is_wdir_sep (wchar_t wchar )
340+ {
341+ return wchar == L'/' || wchar == L'\\' ;
342+ }
343+
344+ static const wchar_t * make_relative_to (const wchar_t * path ,
345+ const wchar_t * relative_to , wchar_t * out ,
346+ size_t size )
347+ {
348+ size_t i = wcslen (relative_to ), len ;
349+
350+ /* Is `path` already absolute? */
351+ if (is_wdir_sep (path [0 ]) ||
352+ (iswalpha (path [0 ]) && path [1 ] == L':' && is_wdir_sep (path [2 ])))
353+ return path ;
354+
355+ while (i > 0 && !is_wdir_sep (relative_to [i - 1 ]))
356+ i -- ;
357+
358+ /* Is `relative_to` in the current directory? */
359+ if (!i )
360+ return path ;
361+
362+ len = wcslen (path );
363+ if (i + len + 1 > size ) {
364+ error ("Could not make '%ls' relative to '%ls' (too large)" ,
365+ path , relative_to );
366+ return NULL ;
367+ }
368+
369+ memcpy (out , relative_to , i * sizeof (wchar_t ));
370+ wcscpy (out + i , path );
371+ return out ;
372+ }
373+
374+ /*
375+ * Changes a file symlink to a directory symlink if the target exists and is a
376+ * directory.
377+ */
378+ static enum phantom_symlink_result
379+ process_phantom_symlink (const wchar_t * wtarget , const wchar_t * wlink )
380+ {
381+ HANDLE hnd ;
382+ BY_HANDLE_FILE_INFORMATION fdata ;
383+ wchar_t relative [MAX_LONG_PATH ];
384+ const wchar_t * rel ;
385+
386+ /* check that wlink is still a file symlink */
387+ if ((GetFileAttributesW (wlink )
388+ & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY ))
389+ != FILE_ATTRIBUTE_REPARSE_POINT )
390+ return PHANTOM_SYMLINK_DONE ;
391+
392+ /* make it relative, if necessary */
393+ rel = make_relative_to (wtarget , wlink , relative , ARRAY_SIZE (relative ));
394+ if (!rel )
395+ return PHANTOM_SYMLINK_DONE ;
396+
397+ /* let Windows resolve the link by opening it */
398+ hnd = CreateFileW (rel , 0 ,
399+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE , NULL ,
400+ OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
401+ if (hnd == INVALID_HANDLE_VALUE ) {
402+ errno = err_win_to_posix (GetLastError ());
403+ return PHANTOM_SYMLINK_RETRY ;
404+ }
405+
406+ if (!GetFileInformationByHandle (hnd , & fdata )) {
407+ errno = err_win_to_posix (GetLastError ());
408+ CloseHandle (hnd );
409+ return PHANTOM_SYMLINK_RETRY ;
410+ }
411+ CloseHandle (hnd );
412+
413+ /* if target exists and is a file, we're done */
414+ if (!(fdata .dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ))
415+ return PHANTOM_SYMLINK_DONE ;
416+
417+ /* otherwise recreate the symlink with directory flag */
418+ if (DeleteFileW (wlink ) && CreateSymbolicLinkW (wlink , wtarget , 1 ))
419+ return PHANTOM_SYMLINK_DIRECTORY ;
420+
421+ errno = err_win_to_posix (GetLastError ());
422+ return PHANTOM_SYMLINK_RETRY ;
423+ }
424+
425+ /* keep track of newly created symlinks to non-existing targets */
426+ struct phantom_symlink_info {
427+ struct phantom_symlink_info * next ;
428+ wchar_t * wlink ;
429+ wchar_t * wtarget ;
430+ };
431+
432+ static struct phantom_symlink_info * phantom_symlinks = NULL ;
433+ static CRITICAL_SECTION phantom_symlinks_cs ;
434+
435+ static void process_phantom_symlinks (void )
436+ {
437+ struct phantom_symlink_info * current , * * psi ;
438+ EnterCriticalSection (& phantom_symlinks_cs );
439+ /* process phantom symlinks list */
440+ psi = & phantom_symlinks ;
441+ while ((current = * psi )) {
442+ enum phantom_symlink_result result = process_phantom_symlink (
443+ current -> wtarget , current -> wlink );
444+ if (result == PHANTOM_SYMLINK_RETRY ) {
445+ psi = & current -> next ;
446+ } else {
447+ /* symlink was processed, remove from list */
448+ * psi = current -> next ;
449+ free (current );
450+ /* if symlink was a directory, start over */
451+ if (result == PHANTOM_SYMLINK_DIRECTORY )
452+ psi = & phantom_symlinks ;
453+ }
454+ }
455+ LeaveCriticalSection (& phantom_symlinks_cs );
456+ }
457+
333458/* Normalizes NT paths as returned by some low-level APIs. */
334459static wchar_t * normalize_ntpath (wchar_t * wbuf )
335460{
@@ -513,6 +638,8 @@ int mingw_mkdir(const char *path, int mode)
513638 return -1 ;
514639
515640 ret = _wmkdir (wpath );
641+ if (!ret )
642+ process_phantom_symlinks ();
516643 if (!ret && needs_hiding (path ))
517644 return set_hidden_flag (wpath , 1 );
518645 return ret ;
@@ -2774,6 +2901,42 @@ int symlink(const char *target, const char *link)
27742901 errno = err_win_to_posix (GetLastError ());
27752902 return -1 ;
27762903 }
2904+
2905+ /* convert to directory symlink if target exists */
2906+ switch (process_phantom_symlink (wtarget , wlink )) {
2907+ case PHANTOM_SYMLINK_RETRY : {
2908+ /* if target doesn't exist, add to phantom symlinks list */
2909+ wchar_t wfullpath [MAX_LONG_PATH ];
2910+ struct phantom_symlink_info * psi ;
2911+
2912+ /* convert to absolute path to be independent of cwd */
2913+ len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
2914+ if (!len || len >= MAX_LONG_PATH ) {
2915+ errno = err_win_to_posix (GetLastError ());
2916+ return -1 ;
2917+ }
2918+
2919+ /* over-allocate and fill phantom_symlink_info structure */
2920+ psi = xmalloc (sizeof (struct phantom_symlink_info )
2921+ + sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
2922+ psi -> wlink = (wchar_t * )(psi + 1 );
2923+ wcscpy (psi -> wlink , wfullpath );
2924+ psi -> wtarget = psi -> wlink + len + 1 ;
2925+ wcscpy (psi -> wtarget , wtarget );
2926+
2927+ EnterCriticalSection (& phantom_symlinks_cs );
2928+ psi -> next = phantom_symlinks ;
2929+ phantom_symlinks = psi ;
2930+ LeaveCriticalSection (& phantom_symlinks_cs );
2931+ break ;
2932+ }
2933+ case PHANTOM_SYMLINK_DIRECTORY :
2934+ /* if we created a dir symlink, process other phantom symlinks */
2935+ process_phantom_symlinks ();
2936+ break ;
2937+ default :
2938+ break ;
2939+ }
27772940 return 0 ;
27782941}
27792942
@@ -3734,6 +3897,7 @@ int wmain(int argc, const wchar_t **wargv)
37343897
37353898 /* initialize critical section for waitpid pinfo_t list */
37363899 InitializeCriticalSection (& pinfo_cs );
3900+ InitializeCriticalSection (& phantom_symlinks_cs );
37373901
37383902 /* initialize critical section for fscache */
37393903 InitializeCriticalSection (& fscache_cs );
0 commit comments