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