@@ -288,6 +288,131 @@ int mingw_core_config(const char *var, const char *value, void *cb)
288
288
return 0 ;
289
289
}
290
290
291
+ enum phantom_symlink_result {
292
+ PHANTOM_SYMLINK_RETRY ,
293
+ PHANTOM_SYMLINK_DONE ,
294
+ PHANTOM_SYMLINK_DIRECTORY
295
+ };
296
+
297
+ static inline int is_wdir_sep (wchar_t wchar )
298
+ {
299
+ return wchar == L'/' || wchar == L'\\' ;
300
+ }
301
+
302
+ static const wchar_t * make_relative_to (const wchar_t * path ,
303
+ const wchar_t * relative_to , wchar_t * out ,
304
+ size_t size )
305
+ {
306
+ size_t i = wcslen (relative_to ), len ;
307
+
308
+ /* Is `path` already absolute? */
309
+ if (is_wdir_sep (path [0 ]) ||
310
+ (iswalpha (path [0 ]) && path [1 ] == L':' && is_wdir_sep (path [2 ])))
311
+ return path ;
312
+
313
+ while (i > 0 && !is_wdir_sep (relative_to [i - 1 ]))
314
+ i -- ;
315
+
316
+ /* Is `relative_to` in the current directory? */
317
+ if (!i )
318
+ return path ;
319
+
320
+ len = wcslen (path );
321
+ if (i + len + 1 > size ) {
322
+ error ("Could not make '%S' relative to '%S' (too large)" ,
323
+ path , relative_to );
324
+ return NULL ;
325
+ }
326
+
327
+ memcpy (out , relative_to , i * sizeof (wchar_t ));
328
+ wcscpy (out + i , path );
329
+ return out ;
330
+ }
331
+
332
+ /*
333
+ * Changes a file symlink to a directory symlink if the target exists and is a
334
+ * directory.
335
+ */
336
+ static enum phantom_symlink_result
337
+ process_phantom_symlink (const wchar_t * wtarget , const wchar_t * wlink )
338
+ {
339
+ HANDLE hnd ;
340
+ BY_HANDLE_FILE_INFORMATION fdata ;
341
+ wchar_t relative [MAX_LONG_PATH ];
342
+ const wchar_t * rel ;
343
+
344
+ /* check that wlink is still a file symlink */
345
+ if ((GetFileAttributesW (wlink )
346
+ & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY ))
347
+ != FILE_ATTRIBUTE_REPARSE_POINT )
348
+ return PHANTOM_SYMLINK_DONE ;
349
+
350
+ /* make it relative, if necessary */
351
+ rel = make_relative_to (wtarget , wlink , relative , ARRAY_SIZE (relative ));
352
+ if (!rel )
353
+ return PHANTOM_SYMLINK_DONE ;
354
+
355
+ /* let Windows resolve the link by opening it */
356
+ hnd = CreateFileW (rel , 0 ,
357
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE , NULL ,
358
+ OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
359
+ if (hnd == INVALID_HANDLE_VALUE ) {
360
+ errno = err_win_to_posix (GetLastError ());
361
+ return PHANTOM_SYMLINK_RETRY ;
362
+ }
363
+
364
+ if (!GetFileInformationByHandle (hnd , & fdata )) {
365
+ errno = err_win_to_posix (GetLastError ());
366
+ CloseHandle (hnd );
367
+ return PHANTOM_SYMLINK_RETRY ;
368
+ }
369
+ CloseHandle (hnd );
370
+
371
+ /* if target exists and is a file, we're done */
372
+ if (!(fdata .dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ))
373
+ return PHANTOM_SYMLINK_DONE ;
374
+
375
+ /* otherwise recreate the symlink with directory flag */
376
+ if (DeleteFileW (wlink ) && CreateSymbolicLinkW (wlink , wtarget , 1 ))
377
+ return PHANTOM_SYMLINK_DIRECTORY ;
378
+
379
+ errno = err_win_to_posix (GetLastError ());
380
+ return PHANTOM_SYMLINK_RETRY ;
381
+ }
382
+
383
+ /* keep track of newly created symlinks to non-existing targets */
384
+ struct phantom_symlink_info {
385
+ struct phantom_symlink_info * next ;
386
+ wchar_t * wlink ;
387
+ wchar_t * wtarget ;
388
+ };
389
+
390
+ static struct phantom_symlink_info * phantom_symlinks = NULL ;
391
+ static CRITICAL_SECTION phantom_symlinks_cs ;
392
+
393
+ static void process_phantom_symlinks (void )
394
+ {
395
+ struct phantom_symlink_info * current , * * psi ;
396
+ EnterCriticalSection (& phantom_symlinks_cs );
397
+ /* process phantom symlinks list */
398
+ psi = & phantom_symlinks ;
399
+ while ((current = * psi )) {
400
+ enum phantom_symlink_result result = process_phantom_symlink (
401
+ current -> wtarget , current -> wlink );
402
+ if (result == PHANTOM_SYMLINK_RETRY ) {
403
+ psi = & current -> next ;
404
+ } else {
405
+ /* symlink was processed, remove from list */
406
+ * psi = current -> next ;
407
+ free (current );
408
+ /* if symlink was a directory, start over */
409
+ if (result == PHANTOM_SYMLINK_DIRECTORY )
410
+ psi = & phantom_symlinks ;
411
+ }
412
+ }
413
+ LeaveCriticalSection (& phantom_symlinks_cs );
414
+ }
415
+
291
416
/* Normalizes NT paths as returned by some low-level APIs. */
292
417
static wchar_t * normalize_ntpath (wchar_t * wbuf )
293
418
{
@@ -437,6 +562,8 @@ int mingw_mkdir(const char *path, int mode)
437
562
return -1 ;
438
563
439
564
ret = _wmkdir (wpath );
565
+ if (!ret )
566
+ process_phantom_symlinks ();
440
567
if (!ret && needs_hiding (path ))
441
568
return set_hidden_flag (wpath , 1 );
442
569
return ret ;
@@ -2252,6 +2379,42 @@ int symlink(const char *target, const char *link)
2252
2379
errno = err_win_to_posix (GetLastError ());
2253
2380
return -1 ;
2254
2381
}
2382
+
2383
+ /* convert to directory symlink if target exists */
2384
+ switch (process_phantom_symlink (wtarget , wlink )) {
2385
+ case PHANTOM_SYMLINK_RETRY : {
2386
+ /* if target doesn't exist, add to phantom symlinks list */
2387
+ wchar_t wfullpath [MAX_LONG_PATH ];
2388
+ struct phantom_symlink_info * psi ;
2389
+
2390
+ /* convert to absolute path to be independent of cwd */
2391
+ len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
2392
+ if (!len || len >= MAX_LONG_PATH ) {
2393
+ errno = err_win_to_posix (GetLastError ());
2394
+ return -1 ;
2395
+ }
2396
+
2397
+ /* over-allocate and fill phantom_symlink_info structure */
2398
+ psi = xmalloc (sizeof (struct phantom_symlink_info )
2399
+ + sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
2400
+ psi -> wlink = (wchar_t * )(psi + 1 );
2401
+ wcscpy (psi -> wlink , wfullpath );
2402
+ psi -> wtarget = psi -> wlink + len + 1 ;
2403
+ wcscpy (psi -> wtarget , wtarget );
2404
+
2405
+ EnterCriticalSection (& phantom_symlinks_cs );
2406
+ psi -> next = phantom_symlinks ;
2407
+ phantom_symlinks = psi ;
2408
+ LeaveCriticalSection (& phantom_symlinks_cs );
2409
+ break ;
2410
+ }
2411
+ case PHANTOM_SYMLINK_DIRECTORY :
2412
+ /* if we created a dir symlink, process other phantom symlinks */
2413
+ process_phantom_symlinks ();
2414
+ break ;
2415
+ default :
2416
+ break ;
2417
+ }
2255
2418
return 0 ;
2256
2419
}
2257
2420
@@ -2764,6 +2927,7 @@ int wmain(int argc, const wchar_t **wargv)
2764
2927
2765
2928
/* initialize critical section for waitpid pinfo_t list */
2766
2929
InitializeCriticalSection (& pinfo_cs );
2930
+ InitializeCriticalSection (& phantom_symlinks_cs );
2767
2931
2768
2932
/* initialize critical section for fscache */
2769
2933
InitializeCriticalSection (& fscache_cs );
0 commit comments