@@ -338,6 +338,126 @@ static inline int is_wdir_sep(wchar_t wchar)
338
338
return wchar == L'/' || wchar == L'\\' ;
339
339
}
340
340
341
+ static const wchar_t * make_relative_to (const wchar_t * path ,
342
+ const wchar_t * relative_to , wchar_t * out ,
343
+ size_t size )
344
+ {
345
+ size_t i = wcslen (relative_to ), len ;
346
+
347
+ /* Is `path` already absolute? */
348
+ if (is_wdir_sep (path [0 ]) ||
349
+ (iswalpha (path [0 ]) && path [1 ] == L':' && is_wdir_sep (path [2 ])))
350
+ return path ;
351
+
352
+ while (i > 0 && !is_wdir_sep (relative_to [i - 1 ]))
353
+ i -- ;
354
+
355
+ /* Is `relative_to` in the current directory? */
356
+ if (!i )
357
+ return path ;
358
+
359
+ len = wcslen (path );
360
+ if (i + len + 1 > size ) {
361
+ error ("Could not make '%ls' relative to '%ls' (too large)" ,
362
+ path , relative_to );
363
+ return NULL ;
364
+ }
365
+
366
+ memcpy (out , relative_to , i * sizeof (wchar_t ));
367
+ wcscpy (out + i , path );
368
+ return out ;
369
+ }
370
+
371
+ enum phantom_symlink_result {
372
+ PHANTOM_SYMLINK_RETRY ,
373
+ PHANTOM_SYMLINK_DONE ,
374
+ PHANTOM_SYMLINK_DIRECTORY
375
+ };
376
+
377
+ /*
378
+ * Changes a file symlink to a directory symlink if the target exists and is a
379
+ * directory.
380
+ */
381
+ static enum phantom_symlink_result
382
+ process_phantom_symlink (const wchar_t * wtarget , const wchar_t * wlink )
383
+ {
384
+ HANDLE hnd ;
385
+ BY_HANDLE_FILE_INFORMATION fdata ;
386
+ wchar_t relative [MAX_LONG_PATH ];
387
+ const wchar_t * rel ;
388
+
389
+ /* check that wlink is still a file symlink */
390
+ if ((GetFileAttributesW (wlink )
391
+ & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY ))
392
+ != FILE_ATTRIBUTE_REPARSE_POINT )
393
+ return PHANTOM_SYMLINK_DONE ;
394
+
395
+ /* make it relative, if necessary */
396
+ rel = make_relative_to (wtarget , wlink , relative , ARRAY_SIZE (relative ));
397
+ if (!rel )
398
+ return PHANTOM_SYMLINK_DONE ;
399
+
400
+ /* let Windows resolve the link by opening it */
401
+ hnd = CreateFileW (rel , 0 ,
402
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE , NULL ,
403
+ OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
404
+ if (hnd == INVALID_HANDLE_VALUE ) {
405
+ errno = err_win_to_posix (GetLastError ());
406
+ return PHANTOM_SYMLINK_RETRY ;
407
+ }
408
+
409
+ if (!GetFileInformationByHandle (hnd , & fdata )) {
410
+ errno = err_win_to_posix (GetLastError ());
411
+ CloseHandle (hnd );
412
+ return PHANTOM_SYMLINK_RETRY ;
413
+ }
414
+ CloseHandle (hnd );
415
+
416
+ /* if target exists and is a file, we're done */
417
+ if (!(fdata .dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ))
418
+ return PHANTOM_SYMLINK_DONE ;
419
+
420
+ /* otherwise recreate the symlink with directory flag */
421
+ if (DeleteFileW (wlink ) && CreateSymbolicLinkW (wlink , wtarget , 1 ))
422
+ return PHANTOM_SYMLINK_DIRECTORY ;
423
+
424
+ errno = err_win_to_posix (GetLastError ());
425
+ return PHANTOM_SYMLINK_RETRY ;
426
+ }
427
+
428
+ /* keep track of newly created symlinks to non-existing targets */
429
+ struct phantom_symlink_info {
430
+ struct phantom_symlink_info * next ;
431
+ wchar_t * wlink ;
432
+ wchar_t * wtarget ;
433
+ };
434
+
435
+ static struct phantom_symlink_info * phantom_symlinks = NULL ;
436
+ static CRITICAL_SECTION phantom_symlinks_cs ;
437
+
438
+ static void process_phantom_symlinks (void )
439
+ {
440
+ struct phantom_symlink_info * current , * * psi ;
441
+ EnterCriticalSection (& phantom_symlinks_cs );
442
+ /* process phantom symlinks list */
443
+ psi = & phantom_symlinks ;
444
+ while ((current = * psi )) {
445
+ enum phantom_symlink_result result = process_phantom_symlink (
446
+ current -> wtarget , current -> wlink );
447
+ if (result == PHANTOM_SYMLINK_RETRY ) {
448
+ psi = & current -> next ;
449
+ } else {
450
+ /* symlink was processed, remove from list */
451
+ * psi = current -> next ;
452
+ free (current );
453
+ /* if symlink was a directory, start over */
454
+ if (result == PHANTOM_SYMLINK_DIRECTORY )
455
+ psi = & phantom_symlinks ;
456
+ }
457
+ }
458
+ LeaveCriticalSection (& phantom_symlinks_cs );
459
+ }
460
+
341
461
/* Normalizes NT paths as returned by some low-level APIs. */
342
462
static wchar_t * normalize_ntpath (wchar_t * wbuf )
343
463
{
@@ -521,6 +641,8 @@ int mingw_mkdir(const char *path, int mode UNUSED)
521
641
return -1 ;
522
642
523
643
ret = _wmkdir (wpath );
644
+ if (!ret )
645
+ process_phantom_symlinks ();
524
646
if (!ret && needs_hiding (path ))
525
647
return set_hidden_flag (wpath , 1 );
526
648
return ret ;
@@ -3019,6 +3141,42 @@ int symlink(const char *target, const char *link)
3019
3141
errno = err_win_to_posix (GetLastError ());
3020
3142
return -1 ;
3021
3143
}
3144
+
3145
+ /* convert to directory symlink if target exists */
3146
+ switch (process_phantom_symlink (wtarget , wlink )) {
3147
+ case PHANTOM_SYMLINK_RETRY : {
3148
+ /* if target doesn't exist, add to phantom symlinks list */
3149
+ wchar_t wfullpath [MAX_LONG_PATH ];
3150
+ struct phantom_symlink_info * psi ;
3151
+
3152
+ /* convert to absolute path to be independent of cwd */
3153
+ len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
3154
+ if (!len || len >= MAX_LONG_PATH ) {
3155
+ errno = err_win_to_posix (GetLastError ());
3156
+ return -1 ;
3157
+ }
3158
+
3159
+ /* over-allocate and fill phantom_symlink_info structure */
3160
+ psi = xmalloc (sizeof (struct phantom_symlink_info )
3161
+ + sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
3162
+ psi -> wlink = (wchar_t * )(psi + 1 );
3163
+ wcscpy (psi -> wlink , wfullpath );
3164
+ psi -> wtarget = psi -> wlink + len + 1 ;
3165
+ wcscpy (psi -> wtarget , wtarget );
3166
+
3167
+ EnterCriticalSection (& phantom_symlinks_cs );
3168
+ psi -> next = phantom_symlinks ;
3169
+ phantom_symlinks = psi ;
3170
+ LeaveCriticalSection (& phantom_symlinks_cs );
3171
+ break ;
3172
+ }
3173
+ case PHANTOM_SYMLINK_DIRECTORY :
3174
+ /* if we created a dir symlink, process other phantom symlinks */
3175
+ process_phantom_symlinks ();
3176
+ break ;
3177
+ default :
3178
+ break ;
3179
+ }
3022
3180
return 0 ;
3023
3181
}
3024
3182
@@ -3980,6 +4138,7 @@ int wmain(int argc, const wchar_t **wargv)
3980
4138
3981
4139
/* initialize critical section for waitpid pinfo_t list */
3982
4140
InitializeCriticalSection (& pinfo_cs );
4141
+ InitializeCriticalSection (& phantom_symlinks_cs );
3983
4142
3984
4143
/* initialize critical section for fscache */
3985
4144
InitializeCriticalSection (& fscache_cs );
0 commit comments