@@ -248,6 +248,27 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
248
248
static char * unset_environment_variables ;
249
249
int core_fscache ;
250
250
251
+ int are_long_paths_enabled (void )
252
+ {
253
+ /* default to `false` during initialization */
254
+ static const int fallback = 0 ;
255
+
256
+ static int enabled = -1 ;
257
+
258
+ if (enabled < 0 ) {
259
+ /* avoid infinite recursion */
260
+ if (!the_repository )
261
+ return fallback ;
262
+
263
+ if (the_repository -> config &&
264
+ the_repository -> config -> hash_initialized &&
265
+ git_config_get_bool ("core.longpaths" , & enabled ) < 0 )
266
+ enabled = 0 ;
267
+ }
268
+
269
+ return enabled < 0 ? fallback : enabled ;
270
+ }
271
+
251
272
int mingw_core_config (const char * var , const char * value ,
252
273
const struct config_context * ctx UNUSED ,
253
274
void * cb UNUSED )
@@ -313,8 +334,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
313
334
int mingw_unlink (const char * pathname )
314
335
{
315
336
int ret , tries = 0 ;
316
- wchar_t wpathname [MAX_PATH ];
317
- if (xutftowcs_path (wpathname , pathname ) < 0 )
337
+ wchar_t wpathname [MAX_LONG_PATH ];
338
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
318
339
return -1 ;
319
340
320
341
if (DeleteFileW (wpathname ))
@@ -346,7 +367,7 @@ static int is_dir_empty(const wchar_t *wpath)
346
367
{
347
368
WIN32_FIND_DATAW findbuf ;
348
369
HANDLE handle ;
349
- wchar_t wbuf [MAX_PATH + 2 ];
370
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
350
371
wcscpy (wbuf , wpath );
351
372
wcscat (wbuf , L"\\*" );
352
373
handle = FindFirstFileW (wbuf , & findbuf );
@@ -367,7 +388,7 @@ static int is_dir_empty(const wchar_t *wpath)
367
388
int mingw_rmdir (const char * pathname )
368
389
{
369
390
int ret , tries = 0 ;
370
- wchar_t wpathname [MAX_PATH ];
391
+ wchar_t wpathname [MAX_LONG_PATH ];
371
392
struct stat st ;
372
393
373
394
/*
@@ -389,7 +410,7 @@ int mingw_rmdir(const char *pathname)
389
410
return -1 ;
390
411
}
391
412
392
- if (xutftowcs_path (wpathname , pathname ) < 0 )
413
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
393
414
return -1 ;
394
415
395
416
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -468,15 +489,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
468
489
int mingw_mkdir (const char * path , int mode UNUSED )
469
490
{
470
491
int ret ;
471
- wchar_t wpath [MAX_PATH ];
492
+ wchar_t wpath [MAX_LONG_PATH ];
472
493
473
494
if (!is_valid_win32_path (path , 0 )) {
474
495
errno = EINVAL ;
475
496
return -1 ;
476
497
}
477
498
478
- if (xutftowcs_path (wpath , path ) < 0 )
499
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
500
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
501
+ are_long_paths_enabled ()) < 0 )
479
502
return -1 ;
503
+
480
504
ret = _wmkdir (wpath );
481
505
if (!ret && needs_hiding (path ))
482
506
return set_hidden_flag (wpath , 1 );
@@ -563,7 +587,7 @@ int mingw_open (const char *filename, int oflags, ...)
563
587
va_list args ;
564
588
unsigned mode ;
565
589
int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
566
- wchar_t wfilename [MAX_PATH ];
590
+ wchar_t wfilename [MAX_LONG_PATH ];
567
591
open_fn_t open_fn ;
568
592
569
593
va_start (args , oflags );
@@ -591,7 +615,7 @@ int mingw_open (const char *filename, int oflags, ...)
591
615
592
616
if (filename && !strcmp (filename , "/dev/null" ))
593
617
wcscpy (wfilename , L"nul" );
594
- else if (xutftowcs_path (wfilename , filename ) < 0 )
618
+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
595
619
return -1 ;
596
620
597
621
fd = open_fn (wfilename , oflags , mode );
@@ -649,14 +673,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
649
673
{
650
674
int hide = needs_hiding (filename );
651
675
FILE * file ;
652
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
676
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
653
677
if (filename && !strcmp (filename , "/dev/null" ))
654
678
wcscpy (wfilename , L"nul" );
655
679
else if (!is_valid_win32_path (filename , 1 )) {
656
680
int create = otype && strchr (otype , 'w' );
657
681
errno = create ? EINVAL : ENOENT ;
658
682
return NULL ;
659
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
683
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
660
684
return NULL ;
661
685
662
686
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -678,14 +702,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
678
702
{
679
703
int hide = needs_hiding (filename );
680
704
FILE * file ;
681
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
705
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
682
706
if (filename && !strcmp (filename , "/dev/null" ))
683
707
wcscpy (wfilename , L"nul" );
684
708
else if (!is_valid_win32_path (filename , 1 )) {
685
709
int create = otype && strchr (otype , 'w' );
686
710
errno = create ? EINVAL : ENOENT ;
687
711
return NULL ;
688
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
712
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
689
713
return NULL ;
690
714
691
715
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -735,7 +759,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
735
759
HANDLE h = (HANDLE ) _get_osfhandle (fd );
736
760
if (GetFileType (h ) != FILE_TYPE_PIPE ) {
737
761
if (orig == EINVAL ) {
738
- wchar_t path [MAX_PATH ];
762
+ wchar_t path [MAX_LONG_PATH ];
739
763
DWORD ret = GetFinalPathNameByHandleW (h , path ,
740
764
ARRAY_SIZE (path ), 0 );
741
765
UINT drive_type = ret > 0 && ret < ARRAY_SIZE (path ) ?
@@ -772,27 +796,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
772
796
773
797
int mingw_access (const char * filename , int mode )
774
798
{
775
- wchar_t wfilename [MAX_PATH ];
799
+ wchar_t wfilename [MAX_LONG_PATH ];
776
800
if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
777
801
return 0 ;
778
- if (xutftowcs_path (wfilename , filename ) < 0 )
802
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
779
803
return -1 ;
780
804
/* X_OK is not supported by the MSVCRT version */
781
805
return _waccess (wfilename , mode & ~X_OK );
782
806
}
783
807
808
+ /* cached length of current directory for handle_long_path */
809
+ static int current_directory_len = 0 ;
810
+
784
811
int mingw_chdir (const char * dirname )
785
812
{
786
- wchar_t wdirname [MAX_PATH ];
787
- if (xutftowcs_path (wdirname , dirname ) < 0 )
813
+ int result ;
814
+ wchar_t wdirname [MAX_LONG_PATH ];
815
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
788
816
return -1 ;
789
- return _wchdir (wdirname );
817
+ result = _wchdir (wdirname );
818
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
819
+ return result ;
790
820
}
791
821
792
822
int mingw_chmod (const char * filename , int mode )
793
823
{
794
- wchar_t wfilename [MAX_PATH ];
795
- if (xutftowcs_path (wfilename , filename ) < 0 )
824
+ wchar_t wfilename [MAX_LONG_PATH ];
825
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
796
826
return -1 ;
797
827
return _wchmod (wfilename , mode );
798
828
}
@@ -840,8 +870,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
840
870
static int do_lstat (int follow , const char * file_name , struct stat * buf )
841
871
{
842
872
WIN32_FILE_ATTRIBUTE_DATA fdata ;
843
- wchar_t wfilename [MAX_PATH ];
844
- if (xutftowcs_path (wfilename , file_name ) < 0 )
873
+ wchar_t wfilename [MAX_LONG_PATH ];
874
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
845
875
return -1 ;
846
876
847
877
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -1012,10 +1042,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
1012
1042
FILETIME mft , aft ;
1013
1043
int rc ;
1014
1044
DWORD attrs ;
1015
- wchar_t wfilename [MAX_PATH ];
1045
+ wchar_t wfilename [MAX_LONG_PATH ];
1016
1046
HANDLE osfilehandle ;
1017
1047
1018
- if (xutftowcs_path (wfilename , file_name ) < 0 )
1048
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
1019
1049
return -1 ;
1020
1050
1021
1051
/* must have write permission */
@@ -1098,6 +1128,7 @@ char *mingw_mktemp(char *template)
1098
1128
wchar_t wtemplate [MAX_PATH ];
1099
1129
int offset = 0 ;
1100
1130
1131
+ /* we need to return the path, thus no long paths here! */
1101
1132
if (xutftowcs_path (wtemplate , template ) < 0 )
1102
1133
return NULL ;
1103
1134
@@ -1750,6 +1781,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1750
1781
1751
1782
if (* argv && !strcmp (cmd , * argv ))
1752
1783
wcmd [0 ] = L'\0' ;
1784
+ /*
1785
+ * Paths to executables and to the current directory do not support
1786
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1787
+ */
1753
1788
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1754
1789
return -1 ;
1755
1790
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2401,8 +2436,9 @@ int mingw_rename(const char *pold, const char *pnew)
2401
2436
{
2402
2437
DWORD attrs , gle ;
2403
2438
int tries = 0 ;
2404
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2405
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2439
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2440
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2441
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
2406
2442
return -1 ;
2407
2443
2408
2444
/*
@@ -2720,9 +2756,9 @@ int mingw_raise(int sig)
2720
2756
2721
2757
int link (const char * oldpath , const char * newpath )
2722
2758
{
2723
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2724
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2725
- xutftowcs_path (wnewpath , newpath ) < 0 )
2759
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2760
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2761
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2726
2762
return -1 ;
2727
2763
2728
2764
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2790,8 +2826,8 @@ int mingw_is_mount_point(struct strbuf *path)
2790
2826
{
2791
2827
WIN32_FIND_DATAW findbuf = { 0 };
2792
2828
HANDLE handle ;
2793
- wchar_t wfilename [MAX_PATH ];
2794
- int wlen = xutftowcs_path (wfilename , path -> buf );
2829
+ wchar_t wfilename [MAX_LONG_PATH ];
2830
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
2795
2831
if (wlen < 0 )
2796
2832
die (_ ("could not get long path for '%s'" ), path -> buf );
2797
2833
@@ -2936,9 +2972,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
2936
2972
2937
2973
static int is_system32_path (const char * path )
2938
2974
{
2939
- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
2975
+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
2940
2976
2941
- if (xutftowcs_path (wpath , path ) < 0 ||
2977
+ if (xutftowcs_long_path (wpath , path ) < 0 ||
2942
2978
!GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
2943
2979
_wcsicmp (system32 , wpath ))
2944
2980
return 0 ;
@@ -3350,6 +3386,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
3350
3386
}
3351
3387
}
3352
3388
3389
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3390
+ {
3391
+ int result ;
3392
+ wchar_t buf [MAX_LONG_PATH ];
3393
+
3394
+ /*
3395
+ * we don't need special handling if path is relative to the current
3396
+ * directory, and current directory + path don't exceed the desired
3397
+ * max_path limit. This should cover > 99 % of cases with minimal
3398
+ * performance impact (git almost always uses relative paths).
3399
+ */
3400
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3401
+ (current_directory_len + len < max_path ))
3402
+ return len ;
3403
+
3404
+ /*
3405
+ * handle everything else:
3406
+ * - absolute paths: "C:\dir\file"
3407
+ * - absolute UNC paths: "\\server\share\dir\file"
3408
+ * - absolute paths on current drive: "\dir\file"
3409
+ * - relative paths on other drive: "X:file"
3410
+ * - prefixed paths: "\\?\...", "\\.\..."
3411
+ */
3412
+
3413
+ /* convert to absolute path using GetFullPathNameW */
3414
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3415
+ if (!result ) {
3416
+ errno = err_win_to_posix (GetLastError ());
3417
+ return -1 ;
3418
+ }
3419
+
3420
+ /*
3421
+ * return absolute path if it fits within max_path (even if
3422
+ * "cwd + path" doesn't due to '..' components)
3423
+ */
3424
+ if (result < max_path ) {
3425
+ wcscpy (path , buf );
3426
+ return result ;
3427
+ }
3428
+
3429
+ /* error out if we shouldn't expand the path or buf is too small */
3430
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3431
+ errno = ENAMETOOLONG ;
3432
+ return -1 ;
3433
+ }
3434
+
3435
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3436
+ if (buf [0 ] == '\\' ) {
3437
+ /* ...unless already prefixed */
3438
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3439
+ return len ;
3440
+
3441
+ wcscpy (path , L"\\\\?\\UNC\\" );
3442
+ wcscpy (path + 8 , buf + 2 );
3443
+ return result + 6 ;
3444
+ } else {
3445
+ wcscpy (path , L"\\\\?\\" );
3446
+ wcscpy (path + 4 , buf );
3447
+ return result + 4 ;
3448
+ }
3449
+ }
3450
+
3353
3451
#if !defined(_MSC_VER )
3354
3452
/*
3355
3453
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3512,6 +3610,9 @@ int wmain(int argc, const wchar_t **wargv)
3512
3610
/* initialize Unicode console */
3513
3611
winansi_init ();
3514
3612
3613
+ /* init length of current directory for handle_long_path */
3614
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3615
+
3515
3616
/* invoke the real main() using our utf8 version of argv. */
3516
3617
exit_status = main (argc , argv );
3517
3618
0 commit comments