@@ -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 ;
@@ -2235,6 +2362,42 @@ int symlink(const char *target, const char *link)
2235
2362
errno = err_win_to_posix (GetLastError ());
2236
2363
return -1 ;
2237
2364
}
2365
+
2366
+ /* convert to directory symlink if target exists */
2367
+ switch (process_phantom_symlink (wtarget , wlink )) {
2368
+ case PHANTOM_SYMLINK_RETRY : {
2369
+ /* if target doesn't exist, add to phantom symlinks list */
2370
+ wchar_t wfullpath [MAX_LONG_PATH ];
2371
+ struct phantom_symlink_info * psi ;
2372
+
2373
+ /* convert to absolute path to be independent of cwd */
2374
+ len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
2375
+ if (!len || len >= MAX_LONG_PATH ) {
2376
+ errno = err_win_to_posix (GetLastError ());
2377
+ return -1 ;
2378
+ }
2379
+
2380
+ /* over-allocate and fill phantom_symlink_info structure */
2381
+ psi = xmalloc (sizeof (struct phantom_symlink_info )
2382
+ + sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
2383
+ psi -> wlink = (wchar_t * )(psi + 1 );
2384
+ wcscpy (psi -> wlink , wfullpath );
2385
+ psi -> wtarget = psi -> wlink + len + 1 ;
2386
+ wcscpy (psi -> wtarget , wtarget );
2387
+
2388
+ EnterCriticalSection (& phantom_symlinks_cs );
2389
+ psi -> next = phantom_symlinks ;
2390
+ phantom_symlinks = psi ;
2391
+ LeaveCriticalSection (& phantom_symlinks_cs );
2392
+ break ;
2393
+ }
2394
+ case PHANTOM_SYMLINK_DIRECTORY :
2395
+ /* if we created a dir symlink, process other phantom symlinks */
2396
+ process_phantom_symlinks ();
2397
+ break ;
2398
+ default :
2399
+ break ;
2400
+ }
2238
2401
return 0 ;
2239
2402
}
2240
2403
@@ -2745,6 +2908,7 @@ int wmain(int argc, const wchar_t **wargv)
2745
2908
2746
2909
/* initialize critical section for waitpid pinfo_t list */
2747
2910
InitializeCriticalSection (& pinfo_cs );
2911
+ InitializeCriticalSection (& phantom_symlinks_cs );
2748
2912
2749
2913
/* set up default file mode and file modes for stdin/out/err */
2750
2914
_fmode = _O_BINARY ;
0 commit comments