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