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