@@ -328,6 +328,126 @@ static inline int is_wdir_sep(wchar_t wchar)
328
328
return wchar == L'/' || wchar == L'\\' ;
329
329
}
330
330
331
+ static const wchar_t * make_relative_to (const wchar_t * path ,
332
+ const wchar_t * relative_to , wchar_t * out ,
333
+ size_t size )
334
+ {
335
+ size_t i = wcslen (relative_to ), len ;
336
+
337
+ /* Is `path` already absolute? */
338
+ if (is_wdir_sep (path [0 ]) ||
339
+ (iswalpha (path [0 ]) && path [1 ] == L':' && is_wdir_sep (path [2 ])))
340
+ return path ;
341
+
342
+ while (i > 0 && !is_wdir_sep (relative_to [i - 1 ]))
343
+ i -- ;
344
+
345
+ /* Is `relative_to` in the current directory? */
346
+ if (!i )
347
+ return path ;
348
+
349
+ len = wcslen (path );
350
+ if (i + len + 1 > size ) {
351
+ error ("Could not make '%ls' relative to '%ls' (too large)" ,
352
+ path , relative_to );
353
+ return NULL ;
354
+ }
355
+
356
+ memcpy (out , relative_to , i * sizeof (wchar_t ));
357
+ wcscpy (out + i , path );
358
+ return out ;
359
+ }
360
+
361
+ enum phantom_symlink_result {
362
+ PHANTOM_SYMLINK_RETRY ,
363
+ PHANTOM_SYMLINK_DONE ,
364
+ PHANTOM_SYMLINK_DIRECTORY
365
+ };
366
+
367
+ /*
368
+ * Changes a file symlink to a directory symlink if the target exists and is a
369
+ * directory.
370
+ */
371
+ static enum phantom_symlink_result
372
+ process_phantom_symlink (const wchar_t * wtarget , const wchar_t * wlink )
373
+ {
374
+ HANDLE hnd ;
375
+ BY_HANDLE_FILE_INFORMATION fdata ;
376
+ wchar_t relative [MAX_LONG_PATH ];
377
+ const wchar_t * rel ;
378
+
379
+ /* check that wlink is still a file symlink */
380
+ if ((GetFileAttributesW (wlink )
381
+ & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY ))
382
+ != FILE_ATTRIBUTE_REPARSE_POINT )
383
+ return PHANTOM_SYMLINK_DONE ;
384
+
385
+ /* make it relative, if necessary */
386
+ rel = make_relative_to (wtarget , wlink , relative , ARRAY_SIZE (relative ));
387
+ if (!rel )
388
+ return PHANTOM_SYMLINK_DONE ;
389
+
390
+ /* let Windows resolve the link by opening it */
391
+ hnd = CreateFileW (rel , 0 ,
392
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE , NULL ,
393
+ OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
394
+ if (hnd == INVALID_HANDLE_VALUE ) {
395
+ errno = err_win_to_posix (GetLastError ());
396
+ return PHANTOM_SYMLINK_RETRY ;
397
+ }
398
+
399
+ if (!GetFileInformationByHandle (hnd , & fdata )) {
400
+ errno = err_win_to_posix (GetLastError ());
401
+ CloseHandle (hnd );
402
+ return PHANTOM_SYMLINK_RETRY ;
403
+ }
404
+ CloseHandle (hnd );
405
+
406
+ /* if target exists and is a file, we're done */
407
+ if (!(fdata .dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ))
408
+ return PHANTOM_SYMLINK_DONE ;
409
+
410
+ /* otherwise recreate the symlink with directory flag */
411
+ if (DeleteFileW (wlink ) && CreateSymbolicLinkW (wlink , wtarget , 1 ))
412
+ return PHANTOM_SYMLINK_DIRECTORY ;
413
+
414
+ errno = err_win_to_posix (GetLastError ());
415
+ return PHANTOM_SYMLINK_RETRY ;
416
+ }
417
+
418
+ /* keep track of newly created symlinks to non-existing targets */
419
+ struct phantom_symlink_info {
420
+ struct phantom_symlink_info * next ;
421
+ wchar_t * wlink ;
422
+ wchar_t * wtarget ;
423
+ };
424
+
425
+ static struct phantom_symlink_info * phantom_symlinks = NULL ;
426
+ static CRITICAL_SECTION phantom_symlinks_cs ;
427
+
428
+ static void process_phantom_symlinks (void )
429
+ {
430
+ struct phantom_symlink_info * current , * * psi ;
431
+ EnterCriticalSection (& phantom_symlinks_cs );
432
+ /* process phantom symlinks list */
433
+ psi = & phantom_symlinks ;
434
+ while ((current = * psi )) {
435
+ enum phantom_symlink_result result = process_phantom_symlink (
436
+ current -> wtarget , current -> wlink );
437
+ if (result == PHANTOM_SYMLINK_RETRY ) {
438
+ psi = & current -> next ;
439
+ } else {
440
+ /* symlink was processed, remove from list */
441
+ * psi = current -> next ;
442
+ free (current );
443
+ /* if symlink was a directory, start over */
444
+ if (result == PHANTOM_SYMLINK_DIRECTORY )
445
+ psi = & phantom_symlinks ;
446
+ }
447
+ }
448
+ LeaveCriticalSection (& phantom_symlinks_cs );
449
+ }
450
+
331
451
/* Normalizes NT paths as returned by some low-level APIs. */
332
452
static wchar_t * normalize_ntpath (wchar_t * wbuf )
333
453
{
@@ -511,6 +631,8 @@ int mingw_mkdir(const char *path, int mode UNUSED)
511
631
return -1 ;
512
632
513
633
ret = _wmkdir (wpath );
634
+ if (!ret )
635
+ process_phantom_symlinks ();
514
636
if (!ret && needs_hiding (path ))
515
637
return set_hidden_flag (wpath , 1 );
516
638
return ret ;
@@ -2961,6 +3083,42 @@ int symlink(const char *target, const char *link)
2961
3083
errno = err_win_to_posix (GetLastError ());
2962
3084
return -1 ;
2963
3085
}
3086
+
3087
+ /* convert to directory symlink if target exists */
3088
+ switch (process_phantom_symlink (wtarget , wlink )) {
3089
+ case PHANTOM_SYMLINK_RETRY : {
3090
+ /* if target doesn't exist, add to phantom symlinks list */
3091
+ wchar_t wfullpath [MAX_LONG_PATH ];
3092
+ struct phantom_symlink_info * psi ;
3093
+
3094
+ /* convert to absolute path to be independent of cwd */
3095
+ len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
3096
+ if (!len || len >= MAX_LONG_PATH ) {
3097
+ errno = err_win_to_posix (GetLastError ());
3098
+ return -1 ;
3099
+ }
3100
+
3101
+ /* over-allocate and fill phantom_symlink_info structure */
3102
+ psi = xmalloc (sizeof (struct phantom_symlink_info )
3103
+ + sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
3104
+ psi -> wlink = (wchar_t * )(psi + 1 );
3105
+ wcscpy (psi -> wlink , wfullpath );
3106
+ psi -> wtarget = psi -> wlink + len + 1 ;
3107
+ wcscpy (psi -> wtarget , wtarget );
3108
+
3109
+ EnterCriticalSection (& phantom_symlinks_cs );
3110
+ psi -> next = phantom_symlinks ;
3111
+ phantom_symlinks = psi ;
3112
+ LeaveCriticalSection (& phantom_symlinks_cs );
3113
+ break ;
3114
+ }
3115
+ case PHANTOM_SYMLINK_DIRECTORY :
3116
+ /* if we created a dir symlink, process other phantom symlinks */
3117
+ process_phantom_symlinks ();
3118
+ break ;
3119
+ default :
3120
+ break ;
3121
+ }
2964
3122
return 0 ;
2965
3123
}
2966
3124
@@ -3933,6 +4091,7 @@ int wmain(int argc, const wchar_t **wargv)
3933
4091
3934
4092
/* initialize critical section for waitpid pinfo_t list */
3935
4093
InitializeCriticalSection (& pinfo_cs );
4094
+ InitializeCriticalSection (& phantom_symlinks_cs );
3936
4095
3937
4096
/* initialize critical section for fscache */
3938
4097
InitializeCriticalSection (& fscache_cs );
0 commit comments