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