@@ -249,6 +249,27 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
249
249
static char * unset_environment_variables ;
250
250
int core_fscache ;
251
251
252
+ int are_long_paths_enabled (void )
253
+ {
254
+ /* default to `false` during initialization */
255
+ static const int fallback = 0 ;
256
+
257
+ static int enabled = -1 ;
258
+
259
+ if (enabled < 0 ) {
260
+ /* avoid infinite recursion */
261
+ if (!the_repository )
262
+ return fallback ;
263
+
264
+ if (the_repository -> config &&
265
+ the_repository -> config -> hash_initialized &&
266
+ git_config_get_bool ("core.longpaths" , & enabled ) < 0 )
267
+ enabled = 0 ;
268
+ }
269
+
270
+ return enabled < 0 ? fallback : enabled ;
271
+ }
272
+
252
273
int mingw_core_config (const char * var , const char * value ,
253
274
const struct config_context * ctx UNUSED ,
254
275
void * cb UNUSED )
@@ -314,8 +335,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
314
335
int mingw_unlink (const char * pathname )
315
336
{
316
337
int ret , tries = 0 ;
317
- wchar_t wpathname [MAX_PATH ];
318
- if (xutftowcs_path (wpathname , pathname ) < 0 )
338
+ wchar_t wpathname [MAX_LONG_PATH ];
339
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
319
340
return -1 ;
320
341
321
342
if (DeleteFileW (wpathname ))
@@ -347,7 +368,7 @@ static int is_dir_empty(const wchar_t *wpath)
347
368
{
348
369
WIN32_FIND_DATAW findbuf ;
349
370
HANDLE handle ;
350
- wchar_t wbuf [MAX_PATH + 2 ];
371
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
351
372
wcscpy (wbuf , wpath );
352
373
wcscat (wbuf , L"\\*" );
353
374
handle = FindFirstFileW (wbuf , & findbuf );
@@ -368,7 +389,7 @@ static int is_dir_empty(const wchar_t *wpath)
368
389
int mingw_rmdir (const char * pathname )
369
390
{
370
391
int ret , tries = 0 ;
371
- wchar_t wpathname [MAX_PATH ];
392
+ wchar_t wpathname [MAX_LONG_PATH ];
372
393
struct stat st ;
373
394
374
395
/*
@@ -390,7 +411,7 @@ int mingw_rmdir(const char *pathname)
390
411
return -1 ;
391
412
}
392
413
393
- if (xutftowcs_path (wpathname , pathname ) < 0 )
414
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
394
415
return -1 ;
395
416
396
417
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -469,15 +490,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
469
490
int mingw_mkdir (const char * path , int mode UNUSED )
470
491
{
471
492
int ret ;
472
- wchar_t wpath [MAX_PATH ];
493
+ wchar_t wpath [MAX_LONG_PATH ];
473
494
474
495
if (!is_valid_win32_path (path , 0 )) {
475
496
errno = EINVAL ;
476
497
return -1 ;
477
498
}
478
499
479
- if (xutftowcs_path (wpath , path ) < 0 )
500
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
501
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
502
+ are_long_paths_enabled ()) < 0 )
480
503
return -1 ;
504
+
481
505
ret = _wmkdir (wpath );
482
506
if (!ret && needs_hiding (path ))
483
507
return set_hidden_flag (wpath , 1 );
@@ -639,7 +663,7 @@ int mingw_open (const char *filename, int oflags, ...)
639
663
va_list args ;
640
664
unsigned mode ;
641
665
int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
642
- wchar_t wfilename [MAX_PATH ];
666
+ wchar_t wfilename [MAX_LONG_PATH ];
643
667
open_fn_t open_fn ;
644
668
645
669
va_start (args , oflags );
@@ -669,7 +693,7 @@ int mingw_open (const char *filename, int oflags, ...)
669
693
670
694
if (filename && !strcmp (filename , "/dev/null" ))
671
695
wcscpy (wfilename , L"nul" );
672
- else if (xutftowcs_path (wfilename , filename ) < 0 )
696
+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
673
697
return -1 ;
674
698
675
699
fd = open_fn (wfilename , oflags , mode );
@@ -727,14 +751,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
727
751
{
728
752
int hide = needs_hiding (filename );
729
753
FILE * file ;
730
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
754
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
731
755
if (filename && !strcmp (filename , "/dev/null" ))
732
756
wcscpy (wfilename , L"nul" );
733
757
else if (!is_valid_win32_path (filename , 1 )) {
734
758
int create = otype && strchr (otype , 'w' );
735
759
errno = create ? EINVAL : ENOENT ;
736
760
return NULL ;
737
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
761
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
738
762
return NULL ;
739
763
740
764
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -756,14 +780,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
756
780
{
757
781
int hide = needs_hiding (filename );
758
782
FILE * file ;
759
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
783
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
760
784
if (filename && !strcmp (filename , "/dev/null" ))
761
785
wcscpy (wfilename , L"nul" );
762
786
else if (!is_valid_win32_path (filename , 1 )) {
763
787
int create = otype && strchr (otype , 'w' );
764
788
errno = create ? EINVAL : ENOENT ;
765
789
return NULL ;
766
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
790
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
767
791
return NULL ;
768
792
769
793
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -813,7 +837,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
813
837
HANDLE h = (HANDLE ) _get_osfhandle (fd );
814
838
if (GetFileType (h ) != FILE_TYPE_PIPE ) {
815
839
if (orig == EINVAL ) {
816
- wchar_t path [MAX_PATH ];
840
+ wchar_t path [MAX_LONG_PATH ];
817
841
DWORD ret = GetFinalPathNameByHandleW (h , path ,
818
842
ARRAY_SIZE (path ), 0 );
819
843
UINT drive_type = ret > 0 && ret < ARRAY_SIZE (path ) ?
@@ -850,27 +874,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
850
874
851
875
int mingw_access (const char * filename , int mode )
852
876
{
853
- wchar_t wfilename [MAX_PATH ];
877
+ wchar_t wfilename [MAX_LONG_PATH ];
854
878
if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
855
879
return 0 ;
856
- if (xutftowcs_path (wfilename , filename ) < 0 )
880
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
857
881
return -1 ;
858
882
/* X_OK is not supported by the MSVCRT version */
859
883
return _waccess (wfilename , mode & ~X_OK );
860
884
}
861
885
886
+ /* cached length of current directory for handle_long_path */
887
+ static int current_directory_len = 0 ;
888
+
862
889
int mingw_chdir (const char * dirname )
863
890
{
864
- wchar_t wdirname [MAX_PATH ];
865
- if (xutftowcs_path (wdirname , dirname ) < 0 )
891
+ int result ;
892
+ wchar_t wdirname [MAX_LONG_PATH ];
893
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
866
894
return -1 ;
867
- return _wchdir (wdirname );
895
+ result = _wchdir (wdirname );
896
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
897
+ return result ;
868
898
}
869
899
870
900
int mingw_chmod (const char * filename , int mode )
871
901
{
872
- wchar_t wfilename [MAX_PATH ];
873
- if (xutftowcs_path (wfilename , filename ) < 0 )
902
+ wchar_t wfilename [MAX_LONG_PATH ];
903
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
874
904
return -1 ;
875
905
return _wchmod (wfilename , mode );
876
906
}
@@ -918,8 +948,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
918
948
static int do_lstat (int follow , const char * file_name , struct stat * buf )
919
949
{
920
950
WIN32_FILE_ATTRIBUTE_DATA fdata ;
921
- wchar_t wfilename [MAX_PATH ];
922
- if (xutftowcs_path (wfilename , file_name ) < 0 )
951
+ wchar_t wfilename [MAX_LONG_PATH ];
952
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
923
953
return -1 ;
924
954
925
955
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -1090,10 +1120,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
1090
1120
FILETIME mft , aft ;
1091
1121
int rc ;
1092
1122
DWORD attrs ;
1093
- wchar_t wfilename [MAX_PATH ];
1123
+ wchar_t wfilename [MAX_LONG_PATH ];
1094
1124
HANDLE osfilehandle ;
1095
1125
1096
- if (xutftowcs_path (wfilename , file_name ) < 0 )
1126
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
1097
1127
return -1 ;
1098
1128
1099
1129
/* must have write permission */
@@ -1176,6 +1206,7 @@ char *mingw_mktemp(char *template)
1176
1206
wchar_t wtemplate [MAX_PATH ];
1177
1207
int offset = 0 ;
1178
1208
1209
+ /* we need to return the path, thus no long paths here! */
1179
1210
if (xutftowcs_path (wtemplate , template ) < 0 )
1180
1211
return NULL ;
1181
1212
@@ -1828,6 +1859,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1828
1859
1829
1860
if (* argv && !strcmp (cmd , * argv ))
1830
1861
wcmd [0 ] = L'\0' ;
1862
+ /*
1863
+ * Paths to executables and to the current directory do not support
1864
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1865
+ */
1831
1866
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1832
1867
return -1 ;
1833
1868
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2556,12 +2591,12 @@ int mingw_rename(const char *pold, const char *pnew)
2556
2591
static int supports_file_rename_info_ex = 1 ;
2557
2592
DWORD attrs , gle ;
2558
2593
int tries = 0 ;
2559
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2594
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2560
2595
int wpnew_len ;
2561
2596
2562
- if (xutftowcs_path (wpold , pold ) < 0 )
2597
+ if (xutftowcs_long_path (wpold , pold ) < 0 )
2563
2598
return -1 ;
2564
- wpnew_len = xutftowcs_path (wpnew , pnew );
2599
+ wpnew_len = xutftowcs_long_path (wpnew , pnew );
2565
2600
if (wpnew_len < 0 )
2566
2601
return -1 ;
2567
2602
@@ -2953,9 +2988,9 @@ int mingw_raise(int sig)
2953
2988
2954
2989
int link (const char * oldpath , const char * newpath )
2955
2990
{
2956
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2957
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2958
- xutftowcs_path (wnewpath , newpath ) < 0 )
2991
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2992
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2993
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2959
2994
return -1 ;
2960
2995
2961
2996
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -3023,8 +3058,8 @@ int mingw_is_mount_point(struct strbuf *path)
3023
3058
{
3024
3059
WIN32_FIND_DATAW findbuf = { 0 };
3025
3060
HANDLE handle ;
3026
- wchar_t wfilename [MAX_PATH ];
3027
- int wlen = xutftowcs_path (wfilename , path -> buf );
3061
+ wchar_t wfilename [MAX_LONG_PATH ];
3062
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
3028
3063
if (wlen < 0 )
3029
3064
die (_ ("could not get long path for '%s'" ), path -> buf );
3030
3065
@@ -3169,9 +3204,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
3169
3204
3170
3205
static int is_system32_path (const char * path )
3171
3206
{
3172
- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
3207
+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
3173
3208
3174
- if (xutftowcs_path (wpath , path ) < 0 ||
3209
+ if (xutftowcs_long_path (wpath , path ) < 0 ||
3175
3210
!GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
3176
3211
_wcsicmp (system32 , wpath ))
3177
3212
return 0 ;
@@ -3583,6 +3618,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
3583
3618
}
3584
3619
}
3585
3620
3621
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3622
+ {
3623
+ int result ;
3624
+ wchar_t buf [MAX_LONG_PATH ];
3625
+
3626
+ /*
3627
+ * we don't need special handling if path is relative to the current
3628
+ * directory, and current directory + path don't exceed the desired
3629
+ * max_path limit. This should cover > 99 % of cases with minimal
3630
+ * performance impact (git almost always uses relative paths).
3631
+ */
3632
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3633
+ (current_directory_len + len < max_path ))
3634
+ return len ;
3635
+
3636
+ /*
3637
+ * handle everything else:
3638
+ * - absolute paths: "C:\dir\file"
3639
+ * - absolute UNC paths: "\\server\share\dir\file"
3640
+ * - absolute paths on current drive: "\dir\file"
3641
+ * - relative paths on other drive: "X:file"
3642
+ * - prefixed paths: "\\?\...", "\\.\..."
3643
+ */
3644
+
3645
+ /* convert to absolute path using GetFullPathNameW */
3646
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3647
+ if (!result ) {
3648
+ errno = err_win_to_posix (GetLastError ());
3649
+ return -1 ;
3650
+ }
3651
+
3652
+ /*
3653
+ * return absolute path if it fits within max_path (even if
3654
+ * "cwd + path" doesn't due to '..' components)
3655
+ */
3656
+ if (result < max_path ) {
3657
+ wcscpy (path , buf );
3658
+ return result ;
3659
+ }
3660
+
3661
+ /* error out if we shouldn't expand the path or buf is too small */
3662
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3663
+ errno = ENAMETOOLONG ;
3664
+ return -1 ;
3665
+ }
3666
+
3667
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3668
+ if (buf [0 ] == '\\' ) {
3669
+ /* ...unless already prefixed */
3670
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3671
+ return len ;
3672
+
3673
+ wcscpy (path , L"\\\\?\\UNC\\" );
3674
+ wcscpy (path + 8 , buf + 2 );
3675
+ return result + 6 ;
3676
+ } else {
3677
+ wcscpy (path , L"\\\\?\\" );
3678
+ wcscpy (path + 4 , buf );
3679
+ return result + 4 ;
3680
+ }
3681
+ }
3682
+
3586
3683
#if !defined(_MSC_VER )
3587
3684
/*
3588
3685
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3745,6 +3842,9 @@ int wmain(int argc, const wchar_t **wargv)
3745
3842
/* initialize Unicode console */
3746
3843
winansi_init ();
3747
3844
3845
+ /* init length of current directory for handle_long_path */
3846
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3847
+
3748
3848
/* invoke the real main() using our utf8 version of argv. */
3749
3849
exit_status = main (argc , argv );
3750
3850
0 commit comments