@@ -331,6 +331,126 @@ static inline int is_wdir_sep(wchar_t wchar)
331
331
return wchar == L'/' || wchar == L'\\' ;
332
332
}
333
333
334
+ static const wchar_t * make_relative_to (const wchar_t * path ,
335
+ const wchar_t * relative_to , wchar_t * out ,
336
+ size_t size )
337
+ {
338
+ size_t i = wcslen (relative_to ), len ;
339
+
340
+ /* Is `path` already absolute? */
341
+ if (is_wdir_sep (path [0 ]) ||
342
+ (iswalpha (path [0 ]) && path [1 ] == L':' && is_wdir_sep (path [2 ])))
343
+ return path ;
344
+
345
+ while (i > 0 && !is_wdir_sep (relative_to [i - 1 ]))
346
+ i -- ;
347
+
348
+ /* Is `relative_to` in the current directory? */
349
+ if (!i )
350
+ return path ;
351
+
352
+ len = wcslen (path );
353
+ if (i + len + 1 > size ) {
354
+ error ("Could not make '%ls' relative to '%ls' (too large)" ,
355
+ path , relative_to );
356
+ return NULL ;
357
+ }
358
+
359
+ memcpy (out , relative_to , i * sizeof (wchar_t ));
360
+ wcscpy (out + i , path );
361
+ return out ;
362
+ }
363
+
364
+ enum phantom_symlink_result {
365
+ PHANTOM_SYMLINK_RETRY ,
366
+ PHANTOM_SYMLINK_DONE ,
367
+ PHANTOM_SYMLINK_DIRECTORY
368
+ };
369
+
370
+ /*
371
+ * Changes a file symlink to a directory symlink if the target exists and is a
372
+ * directory.
373
+ */
374
+ static enum phantom_symlink_result
375
+ process_phantom_symlink (const wchar_t * wtarget , const wchar_t * wlink )
376
+ {
377
+ HANDLE hnd ;
378
+ BY_HANDLE_FILE_INFORMATION fdata ;
379
+ wchar_t relative [MAX_LONG_PATH ];
380
+ const wchar_t * rel ;
381
+
382
+ /* check that wlink is still a file symlink */
383
+ if ((GetFileAttributesW (wlink )
384
+ & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY ))
385
+ != FILE_ATTRIBUTE_REPARSE_POINT )
386
+ return PHANTOM_SYMLINK_DONE ;
387
+
388
+ /* make it relative, if necessary */
389
+ rel = make_relative_to (wtarget , wlink , relative , ARRAY_SIZE (relative ));
390
+ if (!rel )
391
+ return PHANTOM_SYMLINK_DONE ;
392
+
393
+ /* let Windows resolve the link by opening it */
394
+ hnd = CreateFileW (rel , 0 ,
395
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE , NULL ,
396
+ OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
397
+ if (hnd == INVALID_HANDLE_VALUE ) {
398
+ errno = err_win_to_posix (GetLastError ());
399
+ return PHANTOM_SYMLINK_RETRY ;
400
+ }
401
+
402
+ if (!GetFileInformationByHandle (hnd , & fdata )) {
403
+ errno = err_win_to_posix (GetLastError ());
404
+ CloseHandle (hnd );
405
+ return PHANTOM_SYMLINK_RETRY ;
406
+ }
407
+ CloseHandle (hnd );
408
+
409
+ /* if target exists and is a file, we're done */
410
+ if (!(fdata .dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ))
411
+ return PHANTOM_SYMLINK_DONE ;
412
+
413
+ /* otherwise recreate the symlink with directory flag */
414
+ if (DeleteFileW (wlink ) && CreateSymbolicLinkW (wlink , wtarget , 1 ))
415
+ return PHANTOM_SYMLINK_DIRECTORY ;
416
+
417
+ errno = err_win_to_posix (GetLastError ());
418
+ return PHANTOM_SYMLINK_RETRY ;
419
+ }
420
+
421
+ /* keep track of newly created symlinks to non-existing targets */
422
+ struct phantom_symlink_info {
423
+ struct phantom_symlink_info * next ;
424
+ wchar_t * wlink ;
425
+ wchar_t * wtarget ;
426
+ };
427
+
428
+ static struct phantom_symlink_info * phantom_symlinks = NULL ;
429
+ static CRITICAL_SECTION phantom_symlinks_cs ;
430
+
431
+ static void process_phantom_symlinks (void )
432
+ {
433
+ struct phantom_symlink_info * current , * * psi ;
434
+ EnterCriticalSection (& phantom_symlinks_cs );
435
+ /* process phantom symlinks list */
436
+ psi = & phantom_symlinks ;
437
+ while ((current = * psi )) {
438
+ enum phantom_symlink_result result = process_phantom_symlink (
439
+ current -> wtarget , current -> wlink );
440
+ if (result == PHANTOM_SYMLINK_RETRY ) {
441
+ psi = & current -> next ;
442
+ } else {
443
+ /* symlink was processed, remove from list */
444
+ * psi = current -> next ;
445
+ free (current );
446
+ /* if symlink was a directory, start over */
447
+ if (result == PHANTOM_SYMLINK_DIRECTORY )
448
+ psi = & phantom_symlinks ;
449
+ }
450
+ }
451
+ LeaveCriticalSection (& phantom_symlinks_cs );
452
+ }
453
+
334
454
/* Normalizes NT paths as returned by some low-level APIs. */
335
455
static wchar_t * normalize_ntpath (wchar_t * wbuf )
336
456
{
@@ -517,6 +637,8 @@ int mingw_mkdir(const char *path, int mode UNUSED)
517
637
return -1 ;
518
638
519
639
ret = _wmkdir (wpath );
640
+ if (!ret )
641
+ process_phantom_symlinks ();
520
642
if (!ret && needs_hiding (path ))
521
643
return set_hidden_flag (wpath , 1 );
522
644
return ret ;
@@ -2984,6 +3106,42 @@ int symlink(const char *target, const char *link)
2984
3106
errno = err_win_to_posix (GetLastError ());
2985
3107
return -1 ;
2986
3108
}
3109
+
3110
+ /* convert to directory symlink if target exists */
3111
+ switch (process_phantom_symlink (wtarget , wlink )) {
3112
+ case PHANTOM_SYMLINK_RETRY : {
3113
+ /* if target doesn't exist, add to phantom symlinks list */
3114
+ wchar_t wfullpath [MAX_LONG_PATH ];
3115
+ struct phantom_symlink_info * psi ;
3116
+
3117
+ /* convert to absolute path to be independent of cwd */
3118
+ len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
3119
+ if (!len || len >= MAX_LONG_PATH ) {
3120
+ errno = err_win_to_posix (GetLastError ());
3121
+ return -1 ;
3122
+ }
3123
+
3124
+ /* over-allocate and fill phantom_symlink_info structure */
3125
+ psi = xmalloc (sizeof (struct phantom_symlink_info )
3126
+ + sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
3127
+ psi -> wlink = (wchar_t * )(psi + 1 );
3128
+ wcscpy (psi -> wlink , wfullpath );
3129
+ psi -> wtarget = psi -> wlink + len + 1 ;
3130
+ wcscpy (psi -> wtarget , wtarget );
3131
+
3132
+ EnterCriticalSection (& phantom_symlinks_cs );
3133
+ psi -> next = phantom_symlinks ;
3134
+ phantom_symlinks = psi ;
3135
+ LeaveCriticalSection (& phantom_symlinks_cs );
3136
+ break ;
3137
+ }
3138
+ case PHANTOM_SYMLINK_DIRECTORY :
3139
+ /* if we created a dir symlink, process other phantom symlinks */
3140
+ process_phantom_symlinks ();
3141
+ break ;
3142
+ default :
3143
+ break ;
3144
+ }
2987
3145
return 0 ;
2988
3146
}
2989
3147
@@ -3973,6 +4131,7 @@ int wmain(int argc, const wchar_t **wargv)
3973
4131
3974
4132
/* initialize critical section for waitpid pinfo_t list */
3975
4133
InitializeCriticalSection (& pinfo_cs );
4134
+ InitializeCriticalSection (& phantom_symlinks_cs );
3976
4135
3977
4136
/* initialize critical section for fscache */
3978
4137
InitializeCriticalSection (& fscache_cs );
0 commit comments