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