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