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