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