@@ -330,6 +330,131 @@ int mingw_core_config(const char *var, const char *value,
330
330
return 0 ;
331
331
}
332
332
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
+
333
458
/* Normalizes NT paths as returned by some low-level APIs. */
334
459
static wchar_t * normalize_ntpath (wchar_t * wbuf )
335
460
{
@@ -513,6 +638,8 @@ int mingw_mkdir(const char *path, int mode)
513
638
return -1 ;
514
639
515
640
ret = _wmkdir (wpath );
641
+ if (!ret )
642
+ process_phantom_symlinks ();
516
643
if (!ret && needs_hiding (path ))
517
644
return set_hidden_flag (wpath , 1 );
518
645
return ret ;
@@ -2774,6 +2901,42 @@ int symlink(const char *target, const char *link)
2774
2901
errno = err_win_to_posix (GetLastError ());
2775
2902
return -1 ;
2776
2903
}
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
+ }
2777
2940
return 0 ;
2778
2941
}
2779
2942
@@ -3734,6 +3897,7 @@ int wmain(int argc, const wchar_t **wargv)
3734
3897
3735
3898
/* initialize critical section for waitpid pinfo_t list */
3736
3899
InitializeCriticalSection (& pinfo_cs );
3900
+ InitializeCriticalSection (& phantom_symlinks_cs );
3737
3901
3738
3902
/* initialize critical section for fscache */
3739
3903
InitializeCriticalSection (& fscache_cs );
0 commit comments