@@ -228,6 +228,7 @@ enum hide_dotfiles_type {
228
228
static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY ;
229
229
static char * unset_environment_variables ;
230
230
int core_fscache ;
231
+ int core_long_paths ;
231
232
232
233
int mingw_core_config (const char * var , const char * value , void * cb )
233
234
{
@@ -244,6 +245,11 @@ int mingw_core_config(const char *var, const char *value, void *cb)
244
245
return 0 ;
245
246
}
246
247
248
+ if (!strcmp (var , "core.longpaths" )) {
249
+ core_long_paths = git_config_bool (var , value );
250
+ return 0 ;
251
+ }
252
+
247
253
if (!strcmp (var , "core.unsetenvvars" )) {
248
254
free (unset_environment_variables );
249
255
unset_environment_variables = xstrdup (value );
@@ -281,8 +287,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
281
287
int mingw_unlink (const char * pathname )
282
288
{
283
289
int ret , tries = 0 ;
284
- wchar_t wpathname [MAX_PATH ];
285
- if (xutftowcs_path (wpathname , pathname ) < 0 )
290
+ wchar_t wpathname [MAX_LONG_PATH ];
291
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
286
292
return -1 ;
287
293
288
294
/* read-only files cannot be removed */
@@ -311,7 +317,7 @@ static int is_dir_empty(const wchar_t *wpath)
311
317
{
312
318
WIN32_FIND_DATAW findbuf ;
313
319
HANDLE handle ;
314
- wchar_t wbuf [MAX_PATH + 2 ];
320
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
315
321
wcscpy (wbuf , wpath );
316
322
wcscat (wbuf , L"\\*" );
317
323
handle = FindFirstFileW (wbuf , & findbuf );
@@ -332,8 +338,8 @@ static int is_dir_empty(const wchar_t *wpath)
332
338
int mingw_rmdir (const char * pathname )
333
339
{
334
340
int ret , tries = 0 ;
335
- wchar_t wpathname [MAX_PATH ];
336
- if (xutftowcs_path (wpathname , pathname ) < 0 )
341
+ wchar_t wpathname [MAX_LONG_PATH ];
342
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
337
343
return -1 ;
338
344
339
345
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -408,9 +414,12 @@ static int set_hidden_flag(const wchar_t *path, int set)
408
414
int mingw_mkdir (const char * path , int mode )
409
415
{
410
416
int ret ;
411
- wchar_t wpath [MAX_PATH ];
412
- if (xutftowcs_path (wpath , path ) < 0 )
417
+ wchar_t wpath [MAX_LONG_PATH ];
418
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
419
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
420
+ core_long_paths ) < 0 )
413
421
return -1 ;
422
+
414
423
ret = _wmkdir (wpath );
415
424
if (!ret && needs_hiding (path ))
416
425
return set_hidden_flag (wpath , 1 );
@@ -483,7 +492,7 @@ int mingw_open (const char *filename, int oflags, ...)
483
492
va_list args ;
484
493
unsigned mode ;
485
494
int fd ;
486
- wchar_t wfilename [MAX_PATH ];
495
+ wchar_t wfilename [MAX_LONG_PATH ];
487
496
open_fn_t open_fn ;
488
497
489
498
va_start (args , oflags );
@@ -498,7 +507,7 @@ int mingw_open (const char *filename, int oflags, ...)
498
507
else
499
508
open_fn = _wopen ;
500
509
501
- if (xutftowcs_path (wfilename , filename ) < 0 )
510
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
502
511
return -1 ;
503
512
fd = open_fn (wfilename , oflags , mode );
504
513
@@ -555,10 +564,10 @@ FILE *mingw_fopen (const char *filename, const char *otype)
555
564
{
556
565
int hide = needs_hiding (filename );
557
566
FILE * file ;
558
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
567
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
559
568
if (filename && !strcmp (filename , "/dev/null" ))
560
569
filename = "nul" ;
561
- if (xutftowcs_path (wfilename , filename ) < 0 ||
570
+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
562
571
xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
563
572
return NULL ;
564
573
if (hide && !access (filename , F_OK ) && set_hidden_flag (wfilename , 0 )) {
@@ -577,10 +586,10 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
577
586
{
578
587
int hide = needs_hiding (filename );
579
588
FILE * file ;
580
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
589
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
581
590
if (filename && !strcmp (filename , "/dev/null" ))
582
591
filename = "nul" ;
583
- if (xutftowcs_path (wfilename , filename ) < 0 ||
592
+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
584
593
xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
585
594
return NULL ;
586
595
if (hide && !access (filename , F_OK ) && set_hidden_flag (wfilename , 0 )) {
@@ -634,25 +643,31 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
634
643
635
644
int mingw_access (const char * filename , int mode )
636
645
{
637
- wchar_t wfilename [MAX_PATH ];
638
- if (xutftowcs_path (wfilename , filename ) < 0 )
646
+ wchar_t wfilename [MAX_LONG_PATH ];
647
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
639
648
return -1 ;
640
649
/* X_OK is not supported by the MSVCRT version */
641
650
return _waccess (wfilename , mode & ~X_OK );
642
651
}
643
652
653
+ /* cached length of current directory for handle_long_path */
654
+ static int current_directory_len = 0 ;
655
+
644
656
int mingw_chdir (const char * dirname )
645
657
{
646
- wchar_t wdirname [MAX_PATH ];
647
- if (xutftowcs_path (wdirname , dirname ) < 0 )
658
+ int result ;
659
+ wchar_t wdirname [MAX_LONG_PATH ];
660
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
648
661
return -1 ;
649
- return _wchdir (wdirname );
662
+ result = _wchdir (wdirname );
663
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
664
+ return result ;
650
665
}
651
666
652
667
int mingw_chmod (const char * filename , int mode )
653
668
{
654
- wchar_t wfilename [MAX_PATH ];
655
- if (xutftowcs_path (wfilename , filename ) < 0 )
669
+ wchar_t wfilename [MAX_LONG_PATH ];
670
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
656
671
return -1 ;
657
672
return _wchmod (wfilename , mode );
658
673
}
@@ -700,8 +715,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
700
715
static int do_lstat (int follow , const char * file_name , struct stat * buf )
701
716
{
702
717
WIN32_FILE_ATTRIBUTE_DATA fdata ;
703
- wchar_t wfilename [MAX_PATH ];
704
- if (xutftowcs_path (wfilename , file_name ) < 0 )
718
+ wchar_t wfilename [MAX_LONG_PATH ];
719
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
705
720
return -1 ;
706
721
707
722
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -772,7 +787,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
772
787
static int do_stat_internal (int follow , const char * file_name , struct stat * buf )
773
788
{
774
789
int namelen ;
775
- char alt_name [PATH_MAX ];
790
+ char alt_name [MAX_LONG_PATH ];
776
791
777
792
if (!do_lstat (follow , file_name , buf ))
778
793
return 0 ;
@@ -788,7 +803,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
788
803
return -1 ;
789
804
while (namelen && file_name [namelen - 1 ] == '/' )
790
805
-- namelen ;
791
- if (!namelen || namelen >= PATH_MAX )
806
+ if (!namelen || namelen >= MAX_LONG_PATH )
792
807
return -1 ;
793
808
794
809
memcpy (alt_name , file_name , namelen );
@@ -872,8 +887,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
872
887
FILETIME mft , aft ;
873
888
int fh , rc ;
874
889
DWORD attrs ;
875
- wchar_t wfilename [MAX_PATH ];
876
- if (xutftowcs_path (wfilename , file_name ) < 0 )
890
+ wchar_t wfilename [MAX_LONG_PATH ];
891
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
877
892
return -1 ;
878
893
879
894
/* must have write permission */
@@ -934,6 +949,7 @@ char *mingw_mktemp(char *template)
934
949
wchar_t wtemplate [MAX_PATH ];
935
950
int offset = 0 ;
936
951
952
+ /* we need to return the path, thus no long paths here! */
937
953
if (xutftowcs_path (wtemplate , template ) < 0 )
938
954
return NULL ;
939
955
@@ -1457,6 +1473,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1457
1473
1458
1474
if (* argv && !strcmp (cmd , * argv ))
1459
1475
wcmd [0 ] = L'\0' ;
1476
+ /*
1477
+ * Paths to executables and to the current directory do not support
1478
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1479
+ */
1460
1480
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1461
1481
return -1 ;
1462
1482
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -1855,8 +1875,9 @@ int mingw_rename(const char *pold, const char *pnew)
1855
1875
{
1856
1876
DWORD attrs , gle ;
1857
1877
int tries = 0 ;
1858
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
1859
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
1878
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
1879
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
1880
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
1860
1881
return -1 ;
1861
1882
1862
1883
/*
@@ -2165,9 +2186,9 @@ int mingw_raise(int sig)
2165
2186
2166
2187
int link (const char * oldpath , const char * newpath )
2167
2188
{
2168
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2169
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2170
- xutftowcs_path (wnewpath , newpath ) < 0 )
2189
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2190
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2191
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2171
2192
return -1 ;
2172
2193
2173
2194
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2367,6 +2388,68 @@ static void setup_windows_environment(void)
2367
2388
}
2368
2389
}
2369
2390
2391
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
2392
+ {
2393
+ int result ;
2394
+ wchar_t buf [MAX_LONG_PATH ];
2395
+
2396
+ /*
2397
+ * we don't need special handling if path is relative to the current
2398
+ * directory, and current directory + path don't exceed the desired
2399
+ * max_path limit. This should cover > 99 % of cases with minimal
2400
+ * performance impact (git almost always uses relative paths).
2401
+ */
2402
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
2403
+ (current_directory_len + len < max_path ))
2404
+ return len ;
2405
+
2406
+ /*
2407
+ * handle everything else:
2408
+ * - absolute paths: "C:\dir\file"
2409
+ * - absolute UNC paths: "\\server\share\dir\file"
2410
+ * - absolute paths on current drive: "\dir\file"
2411
+ * - relative paths on other drive: "X:file"
2412
+ * - prefixed paths: "\\?\...", "\\.\..."
2413
+ */
2414
+
2415
+ /* convert to absolute path using GetFullPathNameW */
2416
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
2417
+ if (!result ) {
2418
+ errno = err_win_to_posix (GetLastError ());
2419
+ return -1 ;
2420
+ }
2421
+
2422
+ /*
2423
+ * return absolute path if it fits within max_path (even if
2424
+ * "cwd + path" doesn't due to '..' components)
2425
+ */
2426
+ if (result < max_path ) {
2427
+ wcscpy (path , buf );
2428
+ return result ;
2429
+ }
2430
+
2431
+ /* error out if we shouldn't expand the path or buf is too small */
2432
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
2433
+ errno = ENAMETOOLONG ;
2434
+ return -1 ;
2435
+ }
2436
+
2437
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
2438
+ if (buf [0 ] == '\\' ) {
2439
+ /* ...unless already prefixed */
2440
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
2441
+ return len ;
2442
+
2443
+ wcscpy (path , L"\\\\?\\UNC\\" );
2444
+ wcscpy (path + 8 , buf + 2 );
2445
+ return result + 6 ;
2446
+ } else {
2447
+ wcscpy (path , L"\\\\?\\" );
2448
+ wcscpy (path + 4 , buf );
2449
+ return result + 4 ;
2450
+ }
2451
+ }
2452
+
2370
2453
#if !defined(_MSC_VER )
2371
2454
/*
2372
2455
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -2523,6 +2606,9 @@ int wmain(int argc, const wchar_t **wargv)
2523
2606
/* initialize Unicode console */
2524
2607
winansi_init ();
2525
2608
2609
+ /* init length of current directory for handle_long_path */
2610
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
2611
+
2526
2612
/* invoke the real main() using our utf8 version of argv. */
2527
2613
exit_status = main (argc , argv );
2528
2614
0 commit comments