@@ -301,6 +301,131 @@ int mingw_core_config(const char *var, const char *value, void *cb)
301301 return 0 ;
302302}
303303
304+ enum phantom_symlink_result {
305+ PHANTOM_SYMLINK_RETRY ,
306+ PHANTOM_SYMLINK_DONE ,
307+ PHANTOM_SYMLINK_DIRECTORY
308+ };
309+
310+ static inline int is_wdir_sep (wchar_t wchar )
311+ {
312+ return wchar == L'/' || wchar == L'\\' ;
313+ }
314+
315+ static const wchar_t * make_relative_to (const wchar_t * path ,
316+ const wchar_t * relative_to , wchar_t * out ,
317+ size_t size )
318+ {
319+ size_t i = wcslen (relative_to ), len ;
320+
321+ /* Is `path` already absolute? */
322+ if (is_wdir_sep (path [0 ]) ||
323+ (iswalpha (path [0 ]) && path [1 ] == L':' && is_wdir_sep (path [2 ])))
324+ return path ;
325+
326+ while (i > 0 && !is_wdir_sep (relative_to [i - 1 ]))
327+ i -- ;
328+
329+ /* Is `relative_to` in the current directory? */
330+ if (!i )
331+ return path ;
332+
333+ len = wcslen (path );
334+ if (i + len + 1 > size ) {
335+ error ("Could not make '%ls' relative to '%ls' (too large)" ,
336+ path , relative_to );
337+ return NULL ;
338+ }
339+
340+ memcpy (out , relative_to , i * sizeof (wchar_t ));
341+ wcscpy (out + i , path );
342+ return out ;
343+ }
344+
345+ /*
346+ * Changes a file symlink to a directory symlink if the target exists and is a
347+ * directory.
348+ */
349+ static enum phantom_symlink_result
350+ process_phantom_symlink (const wchar_t * wtarget , const wchar_t * wlink )
351+ {
352+ HANDLE hnd ;
353+ BY_HANDLE_FILE_INFORMATION fdata ;
354+ wchar_t relative [MAX_LONG_PATH ];
355+ const wchar_t * rel ;
356+
357+ /* check that wlink is still a file symlink */
358+ if ((GetFileAttributesW (wlink )
359+ & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY ))
360+ != FILE_ATTRIBUTE_REPARSE_POINT )
361+ return PHANTOM_SYMLINK_DONE ;
362+
363+ /* make it relative, if necessary */
364+ rel = make_relative_to (wtarget , wlink , relative , ARRAY_SIZE (relative ));
365+ if (!rel )
366+ return PHANTOM_SYMLINK_DONE ;
367+
368+ /* let Windows resolve the link by opening it */
369+ hnd = CreateFileW (rel , 0 ,
370+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE , NULL ,
371+ OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
372+ if (hnd == INVALID_HANDLE_VALUE ) {
373+ errno = err_win_to_posix (GetLastError ());
374+ return PHANTOM_SYMLINK_RETRY ;
375+ }
376+
377+ if (!GetFileInformationByHandle (hnd , & fdata )) {
378+ errno = err_win_to_posix (GetLastError ());
379+ CloseHandle (hnd );
380+ return PHANTOM_SYMLINK_RETRY ;
381+ }
382+ CloseHandle (hnd );
383+
384+ /* if target exists and is a file, we're done */
385+ if (!(fdata .dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ))
386+ return PHANTOM_SYMLINK_DONE ;
387+
388+ /* otherwise recreate the symlink with directory flag */
389+ if (DeleteFileW (wlink ) && CreateSymbolicLinkW (wlink , wtarget , 1 ))
390+ return PHANTOM_SYMLINK_DIRECTORY ;
391+
392+ errno = err_win_to_posix (GetLastError ());
393+ return PHANTOM_SYMLINK_RETRY ;
394+ }
395+
396+ /* keep track of newly created symlinks to non-existing targets */
397+ struct phantom_symlink_info {
398+ struct phantom_symlink_info * next ;
399+ wchar_t * wlink ;
400+ wchar_t * wtarget ;
401+ };
402+
403+ static struct phantom_symlink_info * phantom_symlinks = NULL ;
404+ static CRITICAL_SECTION phantom_symlinks_cs ;
405+
406+ static void process_phantom_symlinks (void )
407+ {
408+ struct phantom_symlink_info * current , * * psi ;
409+ EnterCriticalSection (& phantom_symlinks_cs );
410+ /* process phantom symlinks list */
411+ psi = & phantom_symlinks ;
412+ while ((current = * psi )) {
413+ enum phantom_symlink_result result = process_phantom_symlink (
414+ current -> wtarget , current -> wlink );
415+ if (result == PHANTOM_SYMLINK_RETRY ) {
416+ psi = & current -> next ;
417+ } else {
418+ /* symlink was processed, remove from list */
419+ * psi = current -> next ;
420+ free (current );
421+ /* if symlink was a directory, start over */
422+ if (result == PHANTOM_SYMLINK_DIRECTORY )
423+ psi = & phantom_symlinks ;
424+ }
425+ }
426+ LeaveCriticalSection (& phantom_symlinks_cs );
427+ }
428+
304429/* Normalizes NT paths as returned by some low-level APIs. */
305430static wchar_t * normalize_ntpath (wchar_t * wbuf )
306431{
@@ -484,6 +609,8 @@ int mingw_mkdir(const char *path, int mode)
484609 return -1 ;
485610
486611 ret = _wmkdir (wpath );
612+ if (!ret )
613+ process_phantom_symlinks ();
487614 if (!ret && needs_hiding (path ))
488615 return set_hidden_flag (wpath , 1 );
489616 return ret ;
@@ -2694,6 +2821,42 @@ int symlink(const char *target, const char *link)
26942821 errno = err_win_to_posix (GetLastError ());
26952822 return -1 ;
26962823 }
2824+
2825+ /* convert to directory symlink if target exists */
2826+ switch (process_phantom_symlink (wtarget , wlink )) {
2827+ case PHANTOM_SYMLINK_RETRY : {
2828+ /* if target doesn't exist, add to phantom symlinks list */
2829+ wchar_t wfullpath [MAX_LONG_PATH ];
2830+ struct phantom_symlink_info * psi ;
2831+
2832+ /* convert to absolute path to be independent of cwd */
2833+ len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
2834+ if (!len || len >= MAX_LONG_PATH ) {
2835+ errno = err_win_to_posix (GetLastError ());
2836+ return -1 ;
2837+ }
2838+
2839+ /* over-allocate and fill phantom_symlink_info structure */
2840+ psi = xmalloc (sizeof (struct phantom_symlink_info )
2841+ + sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
2842+ psi -> wlink = (wchar_t * )(psi + 1 );
2843+ wcscpy (psi -> wlink , wfullpath );
2844+ psi -> wtarget = psi -> wlink + len + 1 ;
2845+ wcscpy (psi -> wtarget , wtarget );
2846+
2847+ EnterCriticalSection (& phantom_symlinks_cs );
2848+ psi -> next = phantom_symlinks ;
2849+ phantom_symlinks = psi ;
2850+ LeaveCriticalSection (& phantom_symlinks_cs );
2851+ break ;
2852+ }
2853+ case PHANTOM_SYMLINK_DIRECTORY :
2854+ /* if we created a dir symlink, process other phantom symlinks */
2855+ process_phantom_symlinks ();
2856+ break ;
2857+ default :
2858+ break ;
2859+ }
26972860 return 0 ;
26982861}
26992862
@@ -3579,6 +3742,7 @@ int wmain(int argc, const wchar_t **wargv)
35793742
35803743 /* initialize critical section for waitpid pinfo_t list */
35813744 InitializeCriticalSection (& pinfo_cs );
3745+ InitializeCriticalSection (& phantom_symlinks_cs );
35823746
35833747 /* initialize critical section for fscache */
35843748 InitializeCriticalSection (& fscache_cs );
0 commit comments