@@ -337,6 +337,126 @@ static inline int is_wdir_sep(wchar_t wchar)
337
337
return wchar == L'/' || wchar == L'\\' ;
338
338
}
339
339
340
+ static const wchar_t * make_relative_to (const wchar_t * path ,
341
+ const wchar_t * relative_to , wchar_t * out ,
342
+ size_t size )
343
+ {
344
+ size_t i = wcslen (relative_to ), len ;
345
+
346
+ /* Is `path` already absolute? */
347
+ if (is_wdir_sep (path [0 ]) ||
348
+ (iswalpha (path [0 ]) && path [1 ] == L':' && is_wdir_sep (path [2 ])))
349
+ return path ;
350
+
351
+ while (i > 0 && !is_wdir_sep (relative_to [i - 1 ]))
352
+ i -- ;
353
+
354
+ /* Is `relative_to` in the current directory? */
355
+ if (!i )
356
+ return path ;
357
+
358
+ len = wcslen (path );
359
+ if (i + len + 1 > size ) {
360
+ error ("Could not make '%ls' relative to '%ls' (too large)" ,
361
+ path , relative_to );
362
+ return NULL ;
363
+ }
364
+
365
+ memcpy (out , relative_to , i * sizeof (wchar_t ));
366
+ wcscpy (out + i , path );
367
+ return out ;
368
+ }
369
+
370
+ enum phantom_symlink_result {
371
+ PHANTOM_SYMLINK_RETRY ,
372
+ PHANTOM_SYMLINK_DONE ,
373
+ PHANTOM_SYMLINK_DIRECTORY
374
+ };
375
+
376
+ /*
377
+ * Changes a file symlink to a directory symlink if the target exists and is a
378
+ * directory.
379
+ */
380
+ static enum phantom_symlink_result
381
+ process_phantom_symlink (const wchar_t * wtarget , const wchar_t * wlink )
382
+ {
383
+ HANDLE hnd ;
384
+ BY_HANDLE_FILE_INFORMATION fdata ;
385
+ wchar_t relative [MAX_LONG_PATH ];
386
+ const wchar_t * rel ;
387
+
388
+ /* check that wlink is still a file symlink */
389
+ if ((GetFileAttributesW (wlink )
390
+ & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY ))
391
+ != FILE_ATTRIBUTE_REPARSE_POINT )
392
+ return PHANTOM_SYMLINK_DONE ;
393
+
394
+ /* make it relative, if necessary */
395
+ rel = make_relative_to (wtarget , wlink , relative , ARRAY_SIZE (relative ));
396
+ if (!rel )
397
+ return PHANTOM_SYMLINK_DONE ;
398
+
399
+ /* let Windows resolve the link by opening it */
400
+ hnd = CreateFileW (rel , 0 ,
401
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE , NULL ,
402
+ OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
403
+ if (hnd == INVALID_HANDLE_VALUE ) {
404
+ errno = err_win_to_posix (GetLastError ());
405
+ return PHANTOM_SYMLINK_RETRY ;
406
+ }
407
+
408
+ if (!GetFileInformationByHandle (hnd , & fdata )) {
409
+ errno = err_win_to_posix (GetLastError ());
410
+ CloseHandle (hnd );
411
+ return PHANTOM_SYMLINK_RETRY ;
412
+ }
413
+ CloseHandle (hnd );
414
+
415
+ /* if target exists and is a file, we're done */
416
+ if (!(fdata .dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ))
417
+ return PHANTOM_SYMLINK_DONE ;
418
+
419
+ /* otherwise recreate the symlink with directory flag */
420
+ if (DeleteFileW (wlink ) && CreateSymbolicLinkW (wlink , wtarget , 1 ))
421
+ return PHANTOM_SYMLINK_DIRECTORY ;
422
+
423
+ errno = err_win_to_posix (GetLastError ());
424
+ return PHANTOM_SYMLINK_RETRY ;
425
+ }
426
+
427
+ /* keep track of newly created symlinks to non-existing targets */
428
+ struct phantom_symlink_info {
429
+ struct phantom_symlink_info * next ;
430
+ wchar_t * wlink ;
431
+ wchar_t * wtarget ;
432
+ };
433
+
434
+ static struct phantom_symlink_info * phantom_symlinks = NULL ;
435
+ static CRITICAL_SECTION phantom_symlinks_cs ;
436
+
437
+ static void process_phantom_symlinks (void )
438
+ {
439
+ struct phantom_symlink_info * current , * * psi ;
440
+ EnterCriticalSection (& phantom_symlinks_cs );
441
+ /* process phantom symlinks list */
442
+ psi = & phantom_symlinks ;
443
+ while ((current = * psi )) {
444
+ enum phantom_symlink_result result = process_phantom_symlink (
445
+ current -> wtarget , current -> wlink );
446
+ if (result == PHANTOM_SYMLINK_RETRY ) {
447
+ psi = & current -> next ;
448
+ } else {
449
+ /* symlink was processed, remove from list */
450
+ * psi = current -> next ;
451
+ free (current );
452
+ /* if symlink was a directory, start over */
453
+ if (result == PHANTOM_SYMLINK_DIRECTORY )
454
+ psi = & phantom_symlinks ;
455
+ }
456
+ }
457
+ LeaveCriticalSection (& phantom_symlinks_cs );
458
+ }
459
+
340
460
/* Normalizes NT paths as returned by some low-level APIs. */
341
461
static wchar_t * normalize_ntpath (wchar_t * wbuf )
342
462
{
@@ -520,6 +640,8 @@ int mingw_mkdir(const char *path, int mode UNUSED)
520
640
return -1 ;
521
641
522
642
ret = _wmkdir (wpath );
643
+ if (!ret )
644
+ process_phantom_symlinks ();
523
645
if (!ret && needs_hiding (path ))
524
646
return set_hidden_flag (wpath , 1 );
525
647
return ret ;
@@ -2931,6 +3053,42 @@ int symlink(const char *target, const char *link)
2931
3053
errno = err_win_to_posix (GetLastError ());
2932
3054
return -1 ;
2933
3055
}
3056
+
3057
+ /* convert to directory symlink if target exists */
3058
+ switch (process_phantom_symlink (wtarget , wlink )) {
3059
+ case PHANTOM_SYMLINK_RETRY : {
3060
+ /* if target doesn't exist, add to phantom symlinks list */
3061
+ wchar_t wfullpath [MAX_LONG_PATH ];
3062
+ struct phantom_symlink_info * psi ;
3063
+
3064
+ /* convert to absolute path to be independent of cwd */
3065
+ len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
3066
+ if (!len || len >= MAX_LONG_PATH ) {
3067
+ errno = err_win_to_posix (GetLastError ());
3068
+ return -1 ;
3069
+ }
3070
+
3071
+ /* over-allocate and fill phantom_symlink_info structure */
3072
+ psi = xmalloc (sizeof (struct phantom_symlink_info )
3073
+ + sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
3074
+ psi -> wlink = (wchar_t * )(psi + 1 );
3075
+ wcscpy (psi -> wlink , wfullpath );
3076
+ psi -> wtarget = psi -> wlink + len + 1 ;
3077
+ wcscpy (psi -> wtarget , wtarget );
3078
+
3079
+ EnterCriticalSection (& phantom_symlinks_cs );
3080
+ psi -> next = phantom_symlinks ;
3081
+ phantom_symlinks = psi ;
3082
+ LeaveCriticalSection (& phantom_symlinks_cs );
3083
+ break ;
3084
+ }
3085
+ case PHANTOM_SYMLINK_DIRECTORY :
3086
+ /* if we created a dir symlink, process other phantom symlinks */
3087
+ process_phantom_symlinks ();
3088
+ break ;
3089
+ default :
3090
+ break ;
3091
+ }
2934
3092
return 0 ;
2935
3093
}
2936
3094
@@ -3892,6 +4050,7 @@ int wmain(int argc, const wchar_t **wargv)
3892
4050
3893
4051
/* initialize critical section for waitpid pinfo_t list */
3894
4052
InitializeCriticalSection (& pinfo_cs );
4053
+ InitializeCriticalSection (& phantom_symlinks_cs );
3895
4054
3896
4055
/* initialize critical section for fscache */
3897
4056
InitializeCriticalSection (& fscache_cs );
0 commit comments