@@ -271,6 +271,131 @@ int mingw_core_config(const char *var, const char *value, void *cb)
271
271
return 0 ;
272
272
}
273
273
274
+ enum phantom_symlink_result {
275
+ PHANTOM_SYMLINK_RETRY ,
276
+ PHANTOM_SYMLINK_DONE ,
277
+ PHANTOM_SYMLINK_DIRECTORY
278
+ };
279
+
280
+ static inline int is_wdir_sep (wchar_t wchar )
281
+ {
282
+ return wchar == L'/' || wchar == L'\\' ;
283
+ }
284
+
285
+ static const wchar_t * make_relative_to (const wchar_t * path ,
286
+ const wchar_t * relative_to , wchar_t * out ,
287
+ size_t size )
288
+ {
289
+ size_t i = wcslen (relative_to ), len ;
290
+
291
+ /* Is `path` already absolute? */
292
+ if (is_wdir_sep (path [0 ]) ||
293
+ (iswalpha (path [0 ]) && path [1 ] == L':' && is_wdir_sep (path [2 ])))
294
+ return path ;
295
+
296
+ while (i > 0 && !is_wdir_sep (relative_to [i - 1 ]))
297
+ i -- ;
298
+
299
+ /* Is `relative_to` in the current directory? */
300
+ if (!i )
301
+ return path ;
302
+
303
+ len = wcslen (path );
304
+ if (i + len + 1 > size ) {
305
+ error ("Could not make '%S' relative to '%S' (too large)" ,
306
+ path , relative_to );
307
+ return NULL ;
308
+ }
309
+
310
+ memcpy (out , relative_to , i * sizeof (wchar_t ));
311
+ wcscpy (out + i , path );
312
+ return out ;
313
+ }
314
+
315
+ /*
316
+ * Changes a file symlink to a directory symlink if the target exists and is a
317
+ * directory.
318
+ */
319
+ static enum phantom_symlink_result
320
+ process_phantom_symlink (const wchar_t * wtarget , const wchar_t * wlink )
321
+ {
322
+ HANDLE hnd ;
323
+ BY_HANDLE_FILE_INFORMATION fdata ;
324
+ wchar_t relative [MAX_LONG_PATH ];
325
+ const wchar_t * rel ;
326
+
327
+ /* check that wlink is still a file symlink */
328
+ if ((GetFileAttributesW (wlink )
329
+ & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY ))
330
+ != FILE_ATTRIBUTE_REPARSE_POINT )
331
+ return PHANTOM_SYMLINK_DONE ;
332
+
333
+ /* make it relative, if necessary */
334
+ rel = make_relative_to (wtarget , wlink , relative , ARRAY_SIZE (relative ));
335
+ if (!rel )
336
+ return PHANTOM_SYMLINK_DONE ;
337
+
338
+ /* let Windows resolve the link by opening it */
339
+ hnd = CreateFileW (rel , 0 ,
340
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE , NULL ,
341
+ OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
342
+ if (hnd == INVALID_HANDLE_VALUE ) {
343
+ errno = err_win_to_posix (GetLastError ());
344
+ return PHANTOM_SYMLINK_RETRY ;
345
+ }
346
+
347
+ if (!GetFileInformationByHandle (hnd , & fdata )) {
348
+ errno = err_win_to_posix (GetLastError ());
349
+ CloseHandle (hnd );
350
+ return PHANTOM_SYMLINK_RETRY ;
351
+ }
352
+ CloseHandle (hnd );
353
+
354
+ /* if target exists and is a file, we're done */
355
+ if (!(fdata .dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ))
356
+ return PHANTOM_SYMLINK_DONE ;
357
+
358
+ /* otherwise recreate the symlink with directory flag */
359
+ if (DeleteFileW (wlink ) && CreateSymbolicLinkW (wlink , wtarget , 1 ))
360
+ return PHANTOM_SYMLINK_DIRECTORY ;
361
+
362
+ errno = err_win_to_posix (GetLastError ());
363
+ return PHANTOM_SYMLINK_RETRY ;
364
+ }
365
+
366
+ /* keep track of newly created symlinks to non-existing targets */
367
+ struct phantom_symlink_info {
368
+ struct phantom_symlink_info * next ;
369
+ wchar_t * wlink ;
370
+ wchar_t * wtarget ;
371
+ };
372
+
373
+ static struct phantom_symlink_info * phantom_symlinks = NULL ;
374
+ static CRITICAL_SECTION phantom_symlinks_cs ;
375
+
376
+ static void process_phantom_symlinks (void )
377
+ {
378
+ struct phantom_symlink_info * current , * * psi ;
379
+ EnterCriticalSection (& phantom_symlinks_cs );
380
+ /* process phantom symlinks list */
381
+ psi = & phantom_symlinks ;
382
+ while ((current = * psi )) {
383
+ enum phantom_symlink_result result = process_phantom_symlink (
384
+ current -> wtarget , current -> wlink );
385
+ if (result == PHANTOM_SYMLINK_RETRY ) {
386
+ psi = & current -> next ;
387
+ } else {
388
+ /* symlink was processed, remove from list */
389
+ * psi = current -> next ;
390
+ free (current );
391
+ /* if symlink was a directory, start over */
392
+ if (result == PHANTOM_SYMLINK_DIRECTORY )
393
+ psi = & phantom_symlinks ;
394
+ }
395
+ }
396
+ LeaveCriticalSection (& phantom_symlinks_cs );
397
+ }
398
+
274
399
/* Normalizes NT paths as returned by some low-level APIs. */
275
400
static wchar_t * normalize_ntpath (wchar_t * wbuf )
276
401
{
@@ -420,6 +545,8 @@ int mingw_mkdir(const char *path, int mode)
420
545
return -1 ;
421
546
422
547
ret = _wmkdir (wpath );
548
+ if (!ret )
549
+ process_phantom_symlinks ();
423
550
if (!ret && needs_hiding (path ))
424
551
return set_hidden_flag (wpath , 1 );
425
552
return ret ;
@@ -2373,6 +2500,42 @@ int symlink(const char *target, const char *link)
2373
2500
errno = err_win_to_posix (GetLastError ());
2374
2501
return -1 ;
2375
2502
}
2503
+
2504
+ /* convert to directory symlink if target exists */
2505
+ switch (process_phantom_symlink (wtarget , wlink )) {
2506
+ case PHANTOM_SYMLINK_RETRY : {
2507
+ /* if target doesn't exist, add to phantom symlinks list */
2508
+ wchar_t wfullpath [MAX_LONG_PATH ];
2509
+ struct phantom_symlink_info * psi ;
2510
+
2511
+ /* convert to absolute path to be independent of cwd */
2512
+ len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
2513
+ if (!len || len >= MAX_LONG_PATH ) {
2514
+ errno = err_win_to_posix (GetLastError ());
2515
+ return -1 ;
2516
+ }
2517
+
2518
+ /* over-allocate and fill phantom_symlink_info structure */
2519
+ psi = xmalloc (sizeof (struct phantom_symlink_info )
2520
+ + sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
2521
+ psi -> wlink = (wchar_t * )(psi + 1 );
2522
+ wcscpy (psi -> wlink , wfullpath );
2523
+ psi -> wtarget = psi -> wlink + len + 1 ;
2524
+ wcscpy (psi -> wtarget , wtarget );
2525
+
2526
+ EnterCriticalSection (& phantom_symlinks_cs );
2527
+ psi -> next = phantom_symlinks ;
2528
+ phantom_symlinks = psi ;
2529
+ LeaveCriticalSection (& phantom_symlinks_cs );
2530
+ break ;
2531
+ }
2532
+ case PHANTOM_SYMLINK_DIRECTORY :
2533
+ /* if we created a dir symlink, process other phantom symlinks */
2534
+ process_phantom_symlinks ();
2535
+ break ;
2536
+ default :
2537
+ break ;
2538
+ }
2376
2539
return 0 ;
2377
2540
}
2378
2541
@@ -2909,6 +3072,7 @@ int wmain(int argc, const wchar_t **wargv)
2909
3072
2910
3073
/* initialize critical section for waitpid pinfo_t list */
2911
3074
InitializeCriticalSection (& pinfo_cs );
3075
+ InitializeCriticalSection (& phantom_symlinks_cs );
2912
3076
2913
3077
/* set up default file mode and file modes for stdin/out/err */
2914
3078
_fmode = _O_BINARY ;
0 commit comments