@@ -272,6 +272,131 @@ int mingw_core_config(const char *var, const char *value, void *cb)
272
272
273
273
DECLARE_PROC_ADDR (kernel32 .dll , BOOLEAN , CreateSymbolicLinkW , LPCWSTR , LPCWSTR , DWORD );
274
274
275
+ enum phantom_symlink_result {
276
+ PHANTOM_SYMLINK_RETRY ,
277
+ PHANTOM_SYMLINK_DONE ,
278
+ PHANTOM_SYMLINK_DIRECTORY
279
+ };
280
+
281
+ static inline int is_wdir_sep (wchar_t wchar )
282
+ {
283
+ return wchar == L'/' || wchar == L'\\' ;
284
+ }
285
+
286
+ static const wchar_t * make_relative_to (const wchar_t * path ,
287
+ const wchar_t * relative_to , wchar_t * out ,
288
+ size_t size )
289
+ {
290
+ size_t i = wcslen (relative_to ), len ;
291
+
292
+ /* Is `path` already absolute? */
293
+ if (is_wdir_sep (path [0 ]) ||
294
+ (iswalpha (path [0 ]) && path [1 ] == L':' && is_wdir_sep (path [2 ])))
295
+ return path ;
296
+
297
+ while (i > 0 && !is_wdir_sep (relative_to [i - 1 ]))
298
+ i -- ;
299
+
300
+ /* Is `relative_to` in the current directory? */
301
+ if (!i )
302
+ return path ;
303
+
304
+ len = wcslen (path );
305
+ if (i + len + 1 > size ) {
306
+ error ("Could not make '%S' relative to '%S' (too large)" ,
307
+ path , relative_to );
308
+ return NULL ;
309
+ }
310
+
311
+ memcpy (out , relative_to , i * sizeof (wchar_t ));
312
+ wcscpy (out + i , path );
313
+ return out ;
314
+ }
315
+
316
+ /*
317
+ * Changes a file symlink to a directory symlink if the target exists and is a
318
+ * directory.
319
+ */
320
+ static enum phantom_symlink_result
321
+ process_phantom_symlink (const wchar_t * wtarget , const wchar_t * wlink )
322
+ {
323
+ HANDLE hnd ;
324
+ BY_HANDLE_FILE_INFORMATION fdata ;
325
+ wchar_t relative [MAX_LONG_PATH ];
326
+ const wchar_t * rel ;
327
+
328
+ /* check that wlink is still a file symlink */
329
+ if ((GetFileAttributesW (wlink )
330
+ & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY ))
331
+ != FILE_ATTRIBUTE_REPARSE_POINT )
332
+ return PHANTOM_SYMLINK_DONE ;
333
+
334
+ /* make it relative, if necessary */
335
+ rel = make_relative_to (wtarget , wlink , relative , ARRAY_SIZE (relative ));
336
+ if (!rel )
337
+ return PHANTOM_SYMLINK_DONE ;
338
+
339
+ /* let Windows resolve the link by opening it */
340
+ hnd = CreateFileW (rel , 0 ,
341
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE , NULL ,
342
+ OPEN_EXISTING , FILE_FLAG_BACKUP_SEMANTICS , NULL );
343
+ if (hnd == INVALID_HANDLE_VALUE ) {
344
+ errno = err_win_to_posix (GetLastError ());
345
+ return PHANTOM_SYMLINK_RETRY ;
346
+ }
347
+
348
+ if (!GetFileInformationByHandle (hnd , & fdata )) {
349
+ errno = err_win_to_posix (GetLastError ());
350
+ CloseHandle (hnd );
351
+ return PHANTOM_SYMLINK_RETRY ;
352
+ }
353
+ CloseHandle (hnd );
354
+
355
+ /* if target exists and is a file, we're done */
356
+ if (!(fdata .dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ))
357
+ return PHANTOM_SYMLINK_DONE ;
358
+
359
+ /* otherwise recreate the symlink with directory flag */
360
+ if (DeleteFileW (wlink ) && CreateSymbolicLinkW (wlink , wtarget , 1 ))
361
+ return PHANTOM_SYMLINK_DIRECTORY ;
362
+
363
+ errno = err_win_to_posix (GetLastError ());
364
+ return PHANTOM_SYMLINK_RETRY ;
365
+ }
366
+
367
+ /* keep track of newly created symlinks to non-existing targets */
368
+ struct phantom_symlink_info {
369
+ struct phantom_symlink_info * next ;
370
+ wchar_t * wlink ;
371
+ wchar_t * wtarget ;
372
+ };
373
+
374
+ static struct phantom_symlink_info * phantom_symlinks = NULL ;
375
+ static CRITICAL_SECTION phantom_symlinks_cs ;
376
+
377
+ static void process_phantom_symlinks (void )
378
+ {
379
+ struct phantom_symlink_info * current , * * psi ;
380
+ EnterCriticalSection (& phantom_symlinks_cs );
381
+ /* process phantom symlinks list */
382
+ psi = & phantom_symlinks ;
383
+ while ((current = * psi )) {
384
+ enum phantom_symlink_result result = process_phantom_symlink (
385
+ current -> wtarget , current -> wlink );
386
+ if (result == PHANTOM_SYMLINK_RETRY ) {
387
+ psi = & current -> next ;
388
+ } else {
389
+ /* symlink was processed, remove from list */
390
+ * psi = current -> next ;
391
+ free (current );
392
+ /* if symlink was a directory, start over */
393
+ if (result == PHANTOM_SYMLINK_DIRECTORY )
394
+ psi = & phantom_symlinks ;
395
+ }
396
+ }
397
+ LeaveCriticalSection (& phantom_symlinks_cs );
398
+ }
399
+
275
400
/* Normalizes NT paths as returned by some low-level APIs. */
276
401
static wchar_t * normalize_ntpath (wchar_t * wbuf )
277
402
{
@@ -424,6 +549,8 @@ int mingw_mkdir(const char *path, int mode)
424
549
return -1 ;
425
550
426
551
ret = _wmkdir (wpath );
552
+ if (!ret )
553
+ process_phantom_symlinks ();
427
554
if (!ret && needs_hiding (path ))
428
555
return set_hidden_flag (wpath , 1 );
429
556
return ret ;
@@ -2199,6 +2326,42 @@ int symlink(const char *target, const char *link)
2199
2326
errno = err_win_to_posix (GetLastError ());
2200
2327
return -1 ;
2201
2328
}
2329
+
2330
+ /* convert to directory symlink if target exists */
2331
+ switch (process_phantom_symlink (wtarget , wlink )) {
2332
+ case PHANTOM_SYMLINK_RETRY : {
2333
+ /* if target doesn't exist, add to phantom symlinks list */
2334
+ wchar_t wfullpath [MAX_LONG_PATH ];
2335
+ struct phantom_symlink_info * psi ;
2336
+
2337
+ /* convert to absolute path to be independent of cwd */
2338
+ len = GetFullPathNameW (wlink , MAX_LONG_PATH , wfullpath , NULL );
2339
+ if (!len || len >= MAX_LONG_PATH ) {
2340
+ errno = err_win_to_posix (GetLastError ());
2341
+ return -1 ;
2342
+ }
2343
+
2344
+ /* over-allocate and fill phantom_symlink_info structure */
2345
+ psi = xmalloc (sizeof (struct phantom_symlink_info )
2346
+ + sizeof (wchar_t ) * (len + wcslen (wtarget ) + 2 ));
2347
+ psi -> wlink = (wchar_t * )(psi + 1 );
2348
+ wcscpy (psi -> wlink , wfullpath );
2349
+ psi -> wtarget = psi -> wlink + len + 1 ;
2350
+ wcscpy (psi -> wtarget , wtarget );
2351
+
2352
+ EnterCriticalSection (& phantom_symlinks_cs );
2353
+ psi -> next = phantom_symlinks ;
2354
+ phantom_symlinks = psi ;
2355
+ LeaveCriticalSection (& phantom_symlinks_cs );
2356
+ break ;
2357
+ }
2358
+ case PHANTOM_SYMLINK_DIRECTORY :
2359
+ /* if we created a dir symlink, process other phantom symlinks */
2360
+ process_phantom_symlinks ();
2361
+ break ;
2362
+ default :
2363
+ break ;
2364
+ }
2202
2365
return 0 ;
2203
2366
}
2204
2367
@@ -2742,6 +2905,7 @@ void mingw_startup(void)
2742
2905
2743
2906
/* initialize critical section for waitpid pinfo_t list */
2744
2907
InitializeCriticalSection (& pinfo_cs );
2908
+ InitializeCriticalSection (& phantom_symlinks_cs );
2745
2909
2746
2910
/* set up default file mode and file modes for stdin/out/err */
2747
2911
_fmode = _O_BINARY ;
0 commit comments