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