@@ -270,6 +270,131 @@ int mingw_core_config(const char *var, const char *value, void *cb)
270
270
return 0 ;
271
271
}
272
272
273
+ enum phantom_symlink_result {
274
+ PHANTOM_SYMLINK_RETRY ,
275
+ PHANTOM_SYMLINK_DONE ,
276
+ PHANTOM_SYMLINK_DIRECTORY
277
+ };
278
+
279
+ static inline int is_wdir_sep (wchar_t wchar )
280
+ {
281
+ return wchar == L'/' || wchar == L'\\' ;
282
+ }
283
+
284
+ static const wchar_t * make_relative_to (const wchar_t * path ,
285
+ const wchar_t * relative_to , wchar_t * out ,
286
+ size_t size )
287
+ {
288
+ size_t i = wcslen (relative_to ), len ;
289
+
290
+ /* Is `path` already absolute? */
291
+ if (is_wdir_sep (path [0 ]) ||
292
+ (iswalpha (path [0 ]) && path [1 ] == L':' && is_wdir_sep (path [2 ])))
293
+ return path ;
294
+
295
+ while (i > 0 && !is_wdir_sep (relative_to [i - 1 ]))
296
+ i -- ;
297
+
298
+ /* Is `relative_to` in the current directory? */
299
+ if (!i )
300
+ return path ;
301
+
302
+ len = wcslen (path );
303
+ if (i + len + 1 > size ) {
304
+ error ("Could not make '%S' relative to '%S' (too large)" ,
305
+ path , relative_to );
306
+ return NULL ;
307
+ }
308
+
309
+ memcpy (out , relative_to , i * sizeof (wchar_t ));
310
+ wcscpy (out + i , path );
311
+ return out ;
312
+ }
313
+
314
+ /*
315
+ * Changes a file symlink to a directory symlink if the target exists and is a
316
+ * directory.
317
+ */
318
+ static enum phantom_symlink_result
319
+ process_phantom_symlink (const wchar_t * wtarget , const wchar_t * wlink )
320
+ {
321
+ HANDLE hnd ;
322
+ BY_HANDLE_FILE_INFORMATION fdata ;
323
+ wchar_t relative [MAX_LONG_PATH ];
324
+ const wchar_t * rel ;
325
+
326
+ /* check that wlink is still a file symlink */
327
+ if ((GetFileAttributesW (wlink )
328
+ & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY ))
329
+ != FILE_ATTRIBUTE_REPARSE_POINT )
330
+ return PHANTOM_SYMLINK_DONE ;
331
+
332
+ /* make it relative, if necessary */
333
+ rel = make_relative_to (wtarget , wlink , relative , ARRAY_SIZE (relative ));
334
+ if (!rel )
335
+ return PHANTOM_SYMLINK_DONE ;
336
+
337
+ /* let Windows resolve the link by opening it */
338
+ hnd = CreateFileW (rel , 0 ,
339
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE , NULL ,
340
+ OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
341
+ if (hnd == INVALID_HANDLE_VALUE ) {
342
+ errno = err_win_to_posix (GetLastError ());
343
+ return PHANTOM_SYMLINK_RETRY ;
344
+ }
345
+
346
+ if (!GetFileInformationByHandle (hnd , & fdata )) {
347
+ errno = err_win_to_posix (GetLastError ());
348
+ CloseHandle (hnd );
349
+ return PHANTOM_SYMLINK_RETRY ;
350
+ }
351
+ CloseHandle (hnd );
352
+
353
+ /* if target exists and is a file, we're done */
354
+ if (!(fdata .dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ))
355
+ return PHANTOM_SYMLINK_DONE ;
356
+
357
+ /* otherwise recreate the symlink with directory flag */
358
+ if (DeleteFileW (wlink ) && CreateSymbolicLinkW (wlink , wtarget , 1 ))
359
+ return PHANTOM_SYMLINK_DIRECTORY ;
360
+
361
+ errno = err_win_to_posix (GetLastError ());
362
+ return PHANTOM_SYMLINK_RETRY ;
363
+ }
364
+
365
+ /* keep track of newly created symlinks to non-existing targets */
366
+ struct phantom_symlink_info {
367
+ struct phantom_symlink_info * next ;
368
+ wchar_t * wlink ;
369
+ wchar_t * wtarget ;
370
+ };
371
+
372
+ static struct phantom_symlink_info * phantom_symlinks = NULL ;
373
+ static CRITICAL_SECTION phantom_symlinks_cs ;
374
+
375
+ static void process_phantom_symlinks (void )
376
+ {
377
+ struct phantom_symlink_info * current , * * psi ;
378
+ EnterCriticalSection (& phantom_symlinks_cs );
379
+ /* process phantom symlinks list */
380
+ psi = & phantom_symlinks ;
381
+ while ((current = * psi )) {
382
+ enum phantom_symlink_result result = process_phantom_symlink (
383
+ current -> wtarget , current -> wlink );
384
+ if (result == PHANTOM_SYMLINK_RETRY ) {
385
+ psi = & current -> next ;
386
+ } else {
387
+ /* symlink was processed, remove from list */
388
+ * psi = current -> next ;
389
+ free (current );
390
+ /* if symlink was a directory, start over */
391
+ if (result == PHANTOM_SYMLINK_DIRECTORY )
392
+ psi = & phantom_symlinks ;
393
+ }
394
+ }
395
+ LeaveCriticalSection (& phantom_symlinks_cs );
396
+ }
397
+
273
398
/* Normalizes NT paths as returned by some low-level APIs. */
274
399
static wchar_t * normalize_ntpath (wchar_t * wbuf )
275
400
{
@@ -419,6 +544,8 @@ int mingw_mkdir(const char *path, int mode)
419
544
return -1 ;
420
545
421
546
ret = _wmkdir (wpath );
547
+ if (!ret )
548
+ process_phantom_symlinks ();
422
549
if (!ret && needs_hiding (path ))
423
550
return set_hidden_flag (wpath , 1 );
424
551
return ret ;
@@ -2289,6 +2416,42 @@ int symlink(const char *target, const char *link)
2289
2416
errno = err_win_to_posix (GetLastError ());
2290
2417
return -1 ;
2291
2418
}
2419
+
2420
+ /* convert to directory symlink if target exists */
2421
+ switch (process_phantom_symlink (wtarget , wlink )) {
2422
+ case PHANTOM_SYMLINK_RETRY : {
2423
+ /* if target doesn't exist, add to phantom symlinks list */
2424
+ wchar_t wfullpath [MAX_LONG_PATH ];
2425
+ struct phantom_symlink_info * psi ;
2426
+
2427
+ /* convert to absolute path to be independent of cwd */
2428
+ len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
2429
+ if (!len || len >= MAX_LONG_PATH ) {
2430
+ errno = err_win_to_posix (GetLastError ());
2431
+ return -1 ;
2432
+ }
2433
+
2434
+ /* over-allocate and fill phantom_symlink_info structure */
2435
+ psi = xmalloc (sizeof (struct phantom_symlink_info )
2436
+ + sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
2437
+ psi -> wlink = (wchar_t * )(psi + 1 );
2438
+ wcscpy (psi -> wlink , wfullpath );
2439
+ psi -> wtarget = psi -> wlink + len + 1 ;
2440
+ wcscpy (psi -> wtarget , wtarget );
2441
+
2442
+ EnterCriticalSection (& phantom_symlinks_cs );
2443
+ psi -> next = phantom_symlinks ;
2444
+ phantom_symlinks = psi ;
2445
+ LeaveCriticalSection (& phantom_symlinks_cs );
2446
+ break ;
2447
+ }
2448
+ case PHANTOM_SYMLINK_DIRECTORY :
2449
+ /* if we created a dir symlink, process other phantom symlinks */
2450
+ process_phantom_symlinks ();
2451
+ break ;
2452
+ default :
2453
+ break ;
2454
+ }
2292
2455
return 0 ;
2293
2456
}
2294
2457
@@ -2834,6 +2997,7 @@ int wmain(int argc, const wchar_t **wargv)
2834
2997
2835
2998
/* initialize critical section for waitpid pinfo_t list */
2836
2999
InitializeCriticalSection (& pinfo_cs );
3000
+ InitializeCriticalSection (& phantom_symlinks_cs );
2837
3001
2838
3002
/* set up default file mode and file modes for stdin/out/err */
2839
3003
_fmode = _O_BINARY ;
0 commit comments