@@ -296,6 +296,131 @@ int mingw_core_config(const char *var, const char *value, void *cb)
296
296
return 0 ;
297
297
}
298
298
299
+ enum phantom_symlink_result {
300
+ PHANTOM_SYMLINK_RETRY ,
301
+ PHANTOM_SYMLINK_DONE ,
302
+ PHANTOM_SYMLINK_DIRECTORY
303
+ };
304
+
305
+ static inline int is_wdir_sep (wchar_t wchar )
306
+ {
307
+ return wchar == L'/' || wchar == L'\\' ;
308
+ }
309
+
310
+ static const wchar_t * make_relative_to (const wchar_t * path ,
311
+ const wchar_t * relative_to , wchar_t * out ,
312
+ size_t size )
313
+ {
314
+ size_t i = wcslen (relative_to ), len ;
315
+
316
+ /* Is `path` already absolute? */
317
+ if (is_wdir_sep (path [0 ]) ||
318
+ (iswalpha (path [0 ]) && path [1 ] == L':' && is_wdir_sep (path [2 ])))
319
+ return path ;
320
+
321
+ while (i > 0 && !is_wdir_sep (relative_to [i - 1 ]))
322
+ i -- ;
323
+
324
+ /* Is `relative_to` in the current directory? */
325
+ if (!i )
326
+ return path ;
327
+
328
+ len = wcslen (path );
329
+ if (i + len + 1 > size ) {
330
+ error ("Could not make '%S' relative to '%S' (too large)" ,
331
+ path , relative_to );
332
+ return NULL ;
333
+ }
334
+
335
+ memcpy (out , relative_to , i * sizeof (wchar_t ));
336
+ wcscpy (out + i , path );
337
+ return out ;
338
+ }
339
+
340
+ /*
341
+ * Changes a file symlink to a directory symlink if the target exists and is a
342
+ * directory.
343
+ */
344
+ static enum phantom_symlink_result
345
+ process_phantom_symlink (const wchar_t * wtarget , const wchar_t * wlink )
346
+ {
347
+ HANDLE hnd ;
348
+ BY_HANDLE_FILE_INFORMATION fdata ;
349
+ wchar_t relative [MAX_LONG_PATH ];
350
+ const wchar_t * rel ;
351
+
352
+ /* check that wlink is still a file symlink */
353
+ if ((GetFileAttributesW (wlink )
354
+ & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY ))
355
+ != FILE_ATTRIBUTE_REPARSE_POINT )
356
+ return PHANTOM_SYMLINK_DONE ;
357
+
358
+ /* make it relative, if necessary */
359
+ rel = make_relative_to (wtarget , wlink , relative , ARRAY_SIZE (relative ));
360
+ if (!rel )
361
+ return PHANTOM_SYMLINK_DONE ;
362
+
363
+ /* let Windows resolve the link by opening it */
364
+ hnd = CreateFileW (rel , 0 ,
365
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE , NULL ,
366
+ OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
367
+ if (hnd == INVALID_HANDLE_VALUE ) {
368
+ errno = err_win_to_posix (GetLastError ());
369
+ return PHANTOM_SYMLINK_RETRY ;
370
+ }
371
+
372
+ if (!GetFileInformationByHandle (hnd , & fdata )) {
373
+ errno = err_win_to_posix (GetLastError ());
374
+ CloseHandle (hnd );
375
+ return PHANTOM_SYMLINK_RETRY ;
376
+ }
377
+ CloseHandle (hnd );
378
+
379
+ /* if target exists and is a file, we're done */
380
+ if (!(fdata .dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ))
381
+ return PHANTOM_SYMLINK_DONE ;
382
+
383
+ /* otherwise recreate the symlink with directory flag */
384
+ if (DeleteFileW (wlink ) && CreateSymbolicLinkW (wlink , wtarget , 1 ))
385
+ return PHANTOM_SYMLINK_DIRECTORY ;
386
+
387
+ errno = err_win_to_posix (GetLastError ());
388
+ return PHANTOM_SYMLINK_RETRY ;
389
+ }
390
+
391
+ /* keep track of newly created symlinks to non-existing targets */
392
+ struct phantom_symlink_info {
393
+ struct phantom_symlink_info * next ;
394
+ wchar_t * wlink ;
395
+ wchar_t * wtarget ;
396
+ };
397
+
398
+ static struct phantom_symlink_info * phantom_symlinks = NULL ;
399
+ static CRITICAL_SECTION phantom_symlinks_cs ;
400
+
401
+ static void process_phantom_symlinks (void )
402
+ {
403
+ struct phantom_symlink_info * current , * * psi ;
404
+ EnterCriticalSection (& phantom_symlinks_cs );
405
+ /* process phantom symlinks list */
406
+ psi = & phantom_symlinks ;
407
+ while ((current = * psi )) {
408
+ enum phantom_symlink_result result = process_phantom_symlink (
409
+ current -> wtarget , current -> wlink );
410
+ if (result == PHANTOM_SYMLINK_RETRY ) {
411
+ psi = & current -> next ;
412
+ } else {
413
+ /* symlink was processed, remove from list */
414
+ * psi = current -> next ;
415
+ free (current );
416
+ /* if symlink was a directory, start over */
417
+ if (result == PHANTOM_SYMLINK_DIRECTORY )
418
+ psi = & phantom_symlinks ;
419
+ }
420
+ }
421
+ LeaveCriticalSection (& phantom_symlinks_cs );
422
+ }
423
+
299
424
/* Normalizes NT paths as returned by some low-level APIs. */
300
425
static wchar_t * normalize_ntpath (wchar_t * wbuf )
301
426
{
@@ -447,6 +572,8 @@ int mingw_mkdir(const char *path, int mode)
447
572
return -1 ;
448
573
449
574
ret = _wmkdir (wpath );
575
+ if (!ret )
576
+ process_phantom_symlinks ();
450
577
if (!ret && needs_hiding (path ))
451
578
return set_hidden_flag (wpath , 1 );
452
579
return ret ;
@@ -2376,6 +2503,42 @@ int symlink(const char *target, const char *link)
2376
2503
errno = err_win_to_posix (GetLastError ());
2377
2504
return -1 ;
2378
2505
}
2506
+
2507
+ /* convert to directory symlink if target exists */
2508
+ switch (process_phantom_symlink (wtarget , wlink )) {
2509
+ case PHANTOM_SYMLINK_RETRY : {
2510
+ /* if target doesn't exist, add to phantom symlinks list */
2511
+ wchar_t wfullpath [MAX_LONG_PATH ];
2512
+ struct phantom_symlink_info * psi ;
2513
+
2514
+ /* convert to absolute path to be independent of cwd */
2515
+ len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
2516
+ if (!len || len >= MAX_LONG_PATH ) {
2517
+ errno = err_win_to_posix (GetLastError ());
2518
+ return -1 ;
2519
+ }
2520
+
2521
+ /* over-allocate and fill phantom_symlink_info structure */
2522
+ psi = xmalloc (sizeof (struct phantom_symlink_info )
2523
+ + sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
2524
+ psi -> wlink = (wchar_t * )(psi + 1 );
2525
+ wcscpy (psi -> wlink , wfullpath );
2526
+ psi -> wtarget = psi -> wlink + len + 1 ;
2527
+ wcscpy (psi -> wtarget , wtarget );
2528
+
2529
+ EnterCriticalSection (& phantom_symlinks_cs );
2530
+ psi -> next = phantom_symlinks ;
2531
+ phantom_symlinks = psi ;
2532
+ LeaveCriticalSection (& phantom_symlinks_cs );
2533
+ break ;
2534
+ }
2535
+ case PHANTOM_SYMLINK_DIRECTORY :
2536
+ /* if we created a dir symlink, process other phantom symlinks */
2537
+ process_phantom_symlinks ();
2538
+ break ;
2539
+ default :
2540
+ break ;
2541
+ }
2379
2542
return 0 ;
2380
2543
}
2381
2544
@@ -2912,6 +3075,7 @@ int wmain(int argc, const wchar_t **wargv)
2912
3075
2913
3076
/* initialize critical section for waitpid pinfo_t list */
2914
3077
InitializeCriticalSection (& pinfo_cs );
3078
+ InitializeCriticalSection (& phantom_symlinks_cs );
2915
3079
2916
3080
/* initialize critical section for fscache */
2917
3081
InitializeCriticalSection (& fscache_cs );
0 commit comments