@@ -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 );
@@ -627,7 +651,7 @@ int mingw_open (const char *filename, int oflags, ...)
627
651
va_list args ;
628
652
unsigned mode ;
629
653
int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
630
- wchar_t wfilename [MAX_PATH ];
654
+ wchar_t wfilename [MAX_LONG_PATH ];
631
655
open_fn_t open_fn ;
632
656
633
657
va_start (args , oflags );
@@ -657,7 +681,7 @@ int mingw_open (const char *filename, int oflags, ...)
657
681
658
682
if (filename && !strcmp (filename , "/dev/null" ))
659
683
wcscpy (wfilename , L"nul" );
660
- else if (xutftowcs_path (wfilename , filename ) < 0 )
684
+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
661
685
return -1 ;
662
686
663
687
fd = open_fn (wfilename , oflags , mode );
@@ -715,14 +739,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
715
739
{
716
740
int hide = needs_hiding (filename );
717
741
FILE * file ;
718
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
742
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
719
743
if (filename && !strcmp (filename , "/dev/null" ))
720
744
wcscpy (wfilename , L"nul" );
721
745
else if (!is_valid_win32_path (filename , 1 )) {
722
746
int create = otype && strchr (otype , 'w' );
723
747
errno = create ? EINVAL : ENOENT ;
724
748
return NULL ;
725
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
749
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
726
750
return NULL ;
727
751
728
752
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -744,14 +768,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
744
768
{
745
769
int hide = needs_hiding (filename );
746
770
FILE * file ;
747
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
771
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
748
772
if (filename && !strcmp (filename , "/dev/null" ))
749
773
wcscpy (wfilename , L"nul" );
750
774
else if (!is_valid_win32_path (filename , 1 )) {
751
775
int create = otype && strchr (otype , 'w' );
752
776
errno = create ? EINVAL : ENOENT ;
753
777
return NULL ;
754
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
778
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
755
779
return NULL ;
756
780
757
781
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -801,7 +825,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
801
825
HANDLE h = (HANDLE ) _get_osfhandle (fd );
802
826
if (GetFileType (h ) != FILE_TYPE_PIPE ) {
803
827
if (orig == EINVAL ) {
804
- wchar_t path [MAX_PATH ];
828
+ wchar_t path [MAX_LONG_PATH ];
805
829
DWORD ret = GetFinalPathNameByHandleW (h , path ,
806
830
ARRAY_SIZE (path ), 0 );
807
831
UINT drive_type = ret > 0 && ret < ARRAY_SIZE (path ) ?
@@ -838,27 +862,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
838
862
839
863
int mingw_access (const char * filename , int mode )
840
864
{
841
- wchar_t wfilename [MAX_PATH ];
865
+ wchar_t wfilename [MAX_LONG_PATH ];
842
866
if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
843
867
return 0 ;
844
- if (xutftowcs_path (wfilename , filename ) < 0 )
868
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
845
869
return -1 ;
846
870
/* X_OK is not supported by the MSVCRT version */
847
871
return _waccess (wfilename , mode & ~X_OK );
848
872
}
849
873
874
+ /* cached length of current directory for handle_long_path */
875
+ static int current_directory_len = 0 ;
876
+
850
877
int mingw_chdir (const char * dirname )
851
878
{
852
- wchar_t wdirname [MAX_PATH ];
853
- if (xutftowcs_path (wdirname , dirname ) < 0 )
879
+ int result ;
880
+ wchar_t wdirname [MAX_LONG_PATH ];
881
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
854
882
return -1 ;
855
- return _wchdir (wdirname );
883
+ result = _wchdir (wdirname );
884
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
885
+ return result ;
856
886
}
857
887
858
888
int mingw_chmod (const char * filename , int mode )
859
889
{
860
- wchar_t wfilename [MAX_PATH ];
861
- if (xutftowcs_path (wfilename , filename ) < 0 )
890
+ wchar_t wfilename [MAX_LONG_PATH ];
891
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
862
892
return -1 ;
863
893
return _wchmod (wfilename , mode );
864
894
}
@@ -906,8 +936,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
906
936
static int do_lstat (int follow , const char * file_name , struct stat * buf )
907
937
{
908
938
WIN32_FILE_ATTRIBUTE_DATA fdata ;
909
- wchar_t wfilename [MAX_PATH ];
910
- if (xutftowcs_path (wfilename , file_name ) < 0 )
939
+ wchar_t wfilename [MAX_LONG_PATH ];
940
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
911
941
return -1 ;
912
942
913
943
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -1078,10 +1108,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
1078
1108
FILETIME mft , aft ;
1079
1109
int rc ;
1080
1110
DWORD attrs ;
1081
- wchar_t wfilename [MAX_PATH ];
1111
+ wchar_t wfilename [MAX_LONG_PATH ];
1082
1112
HANDLE osfilehandle ;
1083
1113
1084
- if (xutftowcs_path (wfilename , file_name ) < 0 )
1114
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
1085
1115
return -1 ;
1086
1116
1087
1117
/* must have write permission */
@@ -1164,6 +1194,7 @@ char *mingw_mktemp(char *template)
1164
1194
wchar_t wtemplate [MAX_PATH ];
1165
1195
int offset = 0 ;
1166
1196
1197
+ /* we need to return the path, thus no long paths here! */
1167
1198
if (xutftowcs_path (wtemplate , template ) < 0 )
1168
1199
return NULL ;
1169
1200
@@ -1816,6 +1847,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1816
1847
1817
1848
if (* argv && !strcmp (cmd , * argv ))
1818
1849
wcmd [0 ] = L'\0' ;
1850
+ /*
1851
+ * Paths to executables and to the current directory do not support
1852
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1853
+ */
1819
1854
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1820
1855
return -1 ;
1821
1856
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2468,12 +2503,12 @@ int mingw_rename(const char *pold, const char *pnew)
2468
2503
static int supports_file_rename_info_ex = 1 ;
2469
2504
DWORD attrs , gle ;
2470
2505
int tries = 0 ;
2471
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2506
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2472
2507
int wpnew_len ;
2473
2508
2474
- if (xutftowcs_path (wpold , pold ) < 0 )
2509
+ if (xutftowcs_long_path (wpold , pold ) < 0 )
2475
2510
return -1 ;
2476
- wpnew_len = xutftowcs_path (wpnew , pnew );
2511
+ wpnew_len = xutftowcs_long_path (wpnew , pnew );
2477
2512
if (wpnew_len < 0 )
2478
2513
return -1 ;
2479
2514
@@ -2865,9 +2900,9 @@ int mingw_raise(int sig)
2865
2900
2866
2901
int link (const char * oldpath , const char * newpath )
2867
2902
{
2868
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2869
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2870
- xutftowcs_path (wnewpath , newpath ) < 0 )
2903
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2904
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2905
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2871
2906
return -1 ;
2872
2907
2873
2908
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2935,8 +2970,8 @@ int mingw_is_mount_point(struct strbuf *path)
2935
2970
{
2936
2971
WIN32_FIND_DATAW findbuf = { 0 };
2937
2972
HANDLE handle ;
2938
- wchar_t wfilename [MAX_PATH ];
2939
- int wlen = xutftowcs_path (wfilename , path -> buf );
2973
+ wchar_t wfilename [MAX_LONG_PATH ];
2974
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
2940
2975
if (wlen < 0 )
2941
2976
die (_ ("could not get long path for '%s'" ), path -> buf );
2942
2977
@@ -3081,9 +3116,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
3081
3116
3082
3117
static int is_system32_path (const char * path )
3083
3118
{
3084
- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
3119
+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
3085
3120
3086
- if (xutftowcs_path (wpath , path ) < 0 ||
3121
+ if (xutftowcs_long_path (wpath , path ) < 0 ||
3087
3122
!GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
3088
3123
_wcsicmp (system32 , wpath ))
3089
3124
return 0 ;
@@ -3495,6 +3530,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
3495
3530
}
3496
3531
}
3497
3532
3533
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3534
+ {
3535
+ int result ;
3536
+ wchar_t buf [MAX_LONG_PATH ];
3537
+
3538
+ /*
3539
+ * we don't need special handling if path is relative to the current
3540
+ * directory, and current directory + path don't exceed the desired
3541
+ * max_path limit. This should cover > 99 % of cases with minimal
3542
+ * performance impact (git almost always uses relative paths).
3543
+ */
3544
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3545
+ (current_directory_len + len < max_path ))
3546
+ return len ;
3547
+
3548
+ /*
3549
+ * handle everything else:
3550
+ * - absolute paths: "C:\dir\file"
3551
+ * - absolute UNC paths: "\\server\share\dir\file"
3552
+ * - absolute paths on current drive: "\dir\file"
3553
+ * - relative paths on other drive: "X:file"
3554
+ * - prefixed paths: "\\?\...", "\\.\..."
3555
+ */
3556
+
3557
+ /* convert to absolute path using GetFullPathNameW */
3558
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3559
+ if (!result ) {
3560
+ errno = err_win_to_posix (GetLastError ());
3561
+ return -1 ;
3562
+ }
3563
+
3564
+ /*
3565
+ * return absolute path if it fits within max_path (even if
3566
+ * "cwd + path" doesn't due to '..' components)
3567
+ */
3568
+ if (result < max_path ) {
3569
+ wcscpy (path , buf );
3570
+ return result ;
3571
+ }
3572
+
3573
+ /* error out if we shouldn't expand the path or buf is too small */
3574
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3575
+ errno = ENAMETOOLONG ;
3576
+ return -1 ;
3577
+ }
3578
+
3579
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3580
+ if (buf [0 ] == '\\' ) {
3581
+ /* ...unless already prefixed */
3582
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3583
+ return len ;
3584
+
3585
+ wcscpy (path , L"\\\\?\\UNC\\" );
3586
+ wcscpy (path + 8 , buf + 2 );
3587
+ return result + 6 ;
3588
+ } else {
3589
+ wcscpy (path , L"\\\\?\\" );
3590
+ wcscpy (path + 4 , buf );
3591
+ return result + 4 ;
3592
+ }
3593
+ }
3594
+
3498
3595
#if !defined(_MSC_VER )
3499
3596
/*
3500
3597
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3657,6 +3754,9 @@ int wmain(int argc, const wchar_t **wargv)
3657
3754
/* initialize Unicode console */
3658
3755
winansi_init ();
3659
3756
3757
+ /* init length of current directory for handle_long_path */
3758
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3759
+
3660
3760
/* invoke the real main() using our utf8 version of argv. */
3661
3761
exit_status = main (argc , argv );
3662
3762
0 commit comments