@@ -297,6 +297,131 @@ int mingw_core_config(const char *var, const char *value, void *cb)
297
297
return 0 ;
298
298
}
299
299
300
+ enum phantom_symlink_result {
301
+ PHANTOM_SYMLINK_RETRY ,
302
+ PHANTOM_SYMLINK_DONE ,
303
+ PHANTOM_SYMLINK_DIRECTORY
304
+ };
305
+
306
+ static inline int is_wdir_sep (wchar_t wchar )
307
+ {
308
+ return wchar == L'/' || wchar == L'\\' ;
309
+ }
310
+
311
+ static const wchar_t * make_relative_to (const wchar_t * path ,
312
+ const wchar_t * relative_to , wchar_t * out ,
313
+ size_t size )
314
+ {
315
+ size_t i = wcslen (relative_to ), len ;
316
+
317
+ /* Is `path` already absolute? */
318
+ if (is_wdir_sep (path [0 ]) ||
319
+ (iswalpha (path [0 ]) && path [1 ] == L':' && is_wdir_sep (path [2 ])))
320
+ return path ;
321
+
322
+ while (i > 0 && !is_wdir_sep (relative_to [i - 1 ]))
323
+ i -- ;
324
+
325
+ /* Is `relative_to` in the current directory? */
326
+ if (!i )
327
+ return path ;
328
+
329
+ len = wcslen (path );
330
+ if (i + len + 1 > size ) {
331
+ error ("Could not make '%S' relative to '%S' (too large)" ,
332
+ path , relative_to );
333
+ return NULL ;
334
+ }
335
+
336
+ memcpy (out , relative_to , i * sizeof (wchar_t ));
337
+ wcscpy (out + i , path );
338
+ return out ;
339
+ }
340
+
341
+ /*
342
+ * Changes a file symlink to a directory symlink if the target exists and is a
343
+ * directory.
344
+ */
345
+ static enum phantom_symlink_result
346
+ process_phantom_symlink (const wchar_t * wtarget , const wchar_t * wlink )
347
+ {
348
+ HANDLE hnd ;
349
+ BY_HANDLE_FILE_INFORMATION fdata ;
350
+ wchar_t relative [MAX_LONG_PATH ];
351
+ const wchar_t * rel ;
352
+
353
+ /* check that wlink is still a file symlink */
354
+ if ((GetFileAttributesW (wlink )
355
+ & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY ))
356
+ != FILE_ATTRIBUTE_REPARSE_POINT )
357
+ return PHANTOM_SYMLINK_DONE ;
358
+
359
+ /* make it relative, if necessary */
360
+ rel = make_relative_to (wtarget , wlink , relative , ARRAY_SIZE (relative ));
361
+ if (!rel )
362
+ return PHANTOM_SYMLINK_DONE ;
363
+
364
+ /* let Windows resolve the link by opening it */
365
+ hnd = CreateFileW (rel , 0 ,
366
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE , NULL ,
367
+ OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
368
+ if (hnd == INVALID_HANDLE_VALUE ) {
369
+ errno = err_win_to_posix (GetLastError ());
370
+ return PHANTOM_SYMLINK_RETRY ;
371
+ }
372
+
373
+ if (!GetFileInformationByHandle (hnd , & fdata )) {
374
+ errno = err_win_to_posix (GetLastError ());
375
+ CloseHandle (hnd );
376
+ return PHANTOM_SYMLINK_RETRY ;
377
+ }
378
+ CloseHandle (hnd );
379
+
380
+ /* if target exists and is a file, we're done */
381
+ if (!(fdata .dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ))
382
+ return PHANTOM_SYMLINK_DONE ;
383
+
384
+ /* otherwise recreate the symlink with directory flag */
385
+ if (DeleteFileW (wlink ) && CreateSymbolicLinkW (wlink , wtarget , 1 ))
386
+ return PHANTOM_SYMLINK_DIRECTORY ;
387
+
388
+ errno = err_win_to_posix (GetLastError ());
389
+ return PHANTOM_SYMLINK_RETRY ;
390
+ }
391
+
392
+ /* keep track of newly created symlinks to non-existing targets */
393
+ struct phantom_symlink_info {
394
+ struct phantom_symlink_info * next ;
395
+ wchar_t * wlink ;
396
+ wchar_t * wtarget ;
397
+ };
398
+
399
+ static struct phantom_symlink_info * phantom_symlinks = NULL ;
400
+ static CRITICAL_SECTION phantom_symlinks_cs ;
401
+
402
+ static void process_phantom_symlinks (void )
403
+ {
404
+ struct phantom_symlink_info * current , * * psi ;
405
+ EnterCriticalSection (& phantom_symlinks_cs );
406
+ /* process phantom symlinks list */
407
+ psi = & phantom_symlinks ;
408
+ while ((current = * psi )) {
409
+ enum phantom_symlink_result result = process_phantom_symlink (
410
+ current -> wtarget , current -> wlink );
411
+ if (result == PHANTOM_SYMLINK_RETRY ) {
412
+ psi = & current -> next ;
413
+ } else {
414
+ /* symlink was processed, remove from list */
415
+ * psi = current -> next ;
416
+ free (current );
417
+ /* if symlink was a directory, start over */
418
+ if (result == PHANTOM_SYMLINK_DIRECTORY )
419
+ psi = & phantom_symlinks ;
420
+ }
421
+ }
422
+ LeaveCriticalSection (& phantom_symlinks_cs );
423
+ }
424
+
300
425
/* Normalizes NT paths as returned by some low-level APIs. */
301
426
static wchar_t * normalize_ntpath (wchar_t * wbuf )
302
427
{
@@ -454,6 +579,8 @@ int mingw_mkdir(const char *path, int mode)
454
579
return -1 ;
455
580
456
581
ret = _wmkdir (wpath );
582
+ if (!ret )
583
+ process_phantom_symlinks ();
457
584
if (!ret && needs_hiding (path ))
458
585
return set_hidden_flag (wpath , 1 );
459
586
return ret ;
@@ -2593,6 +2720,42 @@ int symlink(const char *target, const char *link)
2593
2720
errno = err_win_to_posix (GetLastError ());
2594
2721
return -1 ;
2595
2722
}
2723
+
2724
+ /* convert to directory symlink if target exists */
2725
+ switch (process_phantom_symlink (wtarget , wlink )) {
2726
+ case PHANTOM_SYMLINK_RETRY : {
2727
+ /* if target doesn't exist, add to phantom symlinks list */
2728
+ wchar_t wfullpath [MAX_LONG_PATH ];
2729
+ struct phantom_symlink_info * psi ;
2730
+
2731
+ /* convert to absolute path to be independent of cwd */
2732
+ len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
2733
+ if (!len || len >= MAX_LONG_PATH ) {
2734
+ errno = err_win_to_posix (GetLastError ());
2735
+ return -1 ;
2736
+ }
2737
+
2738
+ /* over-allocate and fill phantom_symlink_info structure */
2739
+ psi = xmalloc (sizeof (struct phantom_symlink_info )
2740
+ + sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
2741
+ psi -> wlink = (wchar_t * )(psi + 1 );
2742
+ wcscpy (psi -> wlink , wfullpath );
2743
+ psi -> wtarget = psi -> wlink + len + 1 ;
2744
+ wcscpy (psi -> wtarget , wtarget );
2745
+
2746
+ EnterCriticalSection (& phantom_symlinks_cs );
2747
+ psi -> next = phantom_symlinks ;
2748
+ phantom_symlinks = psi ;
2749
+ LeaveCriticalSection (& phantom_symlinks_cs );
2750
+ break ;
2751
+ }
2752
+ case PHANTOM_SYMLINK_DIRECTORY :
2753
+ /* if we created a dir symlink, process other phantom symlinks */
2754
+ process_phantom_symlinks ();
2755
+ break ;
2756
+ default :
2757
+ break ;
2758
+ }
2596
2759
return 0 ;
2597
2760
}
2598
2761
@@ -3322,6 +3485,7 @@ int wmain(int argc, const wchar_t **wargv)
3322
3485
3323
3486
/* initialize critical section for waitpid pinfo_t list */
3324
3487
InitializeCriticalSection (& pinfo_cs );
3488
+ InitializeCriticalSection (& phantom_symlinks_cs );
3325
3489
3326
3490
/* initialize critical section for fscache */
3327
3491
InitializeCriticalSection (& fscache_cs );
0 commit comments