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