@@ -252,6 +252,27 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
252
252
static char * unset_environment_variables ;
253
253
int core_fscache ;
254
254
255
+ int are_long_paths_enabled (void )
256
+ {
257
+ /* default to `false` during initialization */
258
+ static const int fallback = 0 ;
259
+
260
+ static int enabled = -1 ;
261
+
262
+ if (enabled < 0 ) {
263
+ /* avoid infinite recursion */
264
+ if (!the_repository )
265
+ return fallback ;
266
+
267
+ if (the_repository -> config &&
268
+ the_repository -> config -> hash_initialized &&
269
+ git_config_get_bool ("core.longpaths" , & enabled ) < 0 )
270
+ enabled = 0 ;
271
+ }
272
+
273
+ return enabled < 0 ? fallback : enabled ;
274
+ }
275
+
255
276
int mingw_core_config (const char * var , const char * value ,
256
277
const struct config_context * ctx UNUSED ,
257
278
void * cb UNUSED )
@@ -308,8 +329,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
308
329
int mingw_unlink (const char * pathname , int handle_in_use_error )
309
330
{
310
331
int ret , tries = 0 ;
311
- wchar_t wpathname [MAX_PATH ];
312
- if (xutftowcs_path (wpathname , pathname ) < 0 )
332
+ wchar_t wpathname [MAX_LONG_PATH ];
333
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
313
334
return -1 ;
314
335
315
336
if (DeleteFileW (wpathname ))
@@ -344,7 +365,7 @@ static int is_dir_empty(const wchar_t *wpath)
344
365
{
345
366
WIN32_FIND_DATAW findbuf ;
346
367
HANDLE handle ;
347
- wchar_t wbuf [MAX_PATH + 2 ];
368
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
348
369
wcscpy (wbuf , wpath );
349
370
wcscat (wbuf , L"\\*" );
350
371
handle = FindFirstFileW (wbuf , & findbuf );
@@ -365,7 +386,7 @@ static int is_dir_empty(const wchar_t *wpath)
365
386
int mingw_rmdir (const char * pathname )
366
387
{
367
388
int ret , tries = 0 ;
368
- wchar_t wpathname [MAX_PATH ];
389
+ wchar_t wpathname [MAX_LONG_PATH ];
369
390
struct stat st ;
370
391
371
392
/*
@@ -387,7 +408,7 @@ int mingw_rmdir(const char *pathname)
387
408
return -1 ;
388
409
}
389
410
390
- if (xutftowcs_path (wpathname , pathname ) < 0 )
411
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
391
412
return -1 ;
392
413
393
414
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -466,15 +487,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
466
487
int mingw_mkdir (const char * path , int mode UNUSED )
467
488
{
468
489
int ret ;
469
- wchar_t wpath [MAX_PATH ];
490
+ wchar_t wpath [MAX_LONG_PATH ];
470
491
471
492
if (!is_valid_win32_path (path , 0 )) {
472
493
errno = EINVAL ;
473
494
return -1 ;
474
495
}
475
496
476
- if (xutftowcs_path (wpath , path ) < 0 )
497
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
498
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
499
+ are_long_paths_enabled ()) < 0 )
477
500
return -1 ;
501
+
478
502
ret = _wmkdir (wpath );
479
503
if (!ret && needs_hiding (path ))
480
504
return set_hidden_flag (wpath , 1 );
@@ -636,7 +660,7 @@ int mingw_open (const char *filename, int oflags, ...)
636
660
va_list args ;
637
661
unsigned mode ;
638
662
int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
639
- wchar_t wfilename [MAX_PATH ];
663
+ wchar_t wfilename [MAX_LONG_PATH ];
640
664
open_fn_t open_fn ;
641
665
642
666
DECLARE_PROC_ADDR (ntdll .dll , NTSTATUS , NTAPI , RtlGetLastNtStatus , void );
@@ -668,7 +692,7 @@ int mingw_open (const char *filename, int oflags, ...)
668
692
669
693
if (filename && !strcmp (filename , "/dev/null" ))
670
694
wcscpy (wfilename , L"nul" );
671
- else if (xutftowcs_path (wfilename , filename ) < 0 )
695
+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
672
696
return -1 ;
673
697
674
698
fd = open_fn (wfilename , oflags , mode );
@@ -741,14 +765,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
741
765
{
742
766
int hide = needs_hiding (filename );
743
767
FILE * file ;
744
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
768
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
745
769
if (filename && !strcmp (filename , "/dev/null" ))
746
770
wcscpy (wfilename , L"nul" );
747
771
else if (!is_valid_win32_path (filename , 1 )) {
748
772
int create = otype && strchr (otype , 'w' );
749
773
errno = create ? EINVAL : ENOENT ;
750
774
return NULL ;
751
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
775
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
752
776
return NULL ;
753
777
754
778
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -770,14 +794,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
770
794
{
771
795
int hide = needs_hiding (filename );
772
796
FILE * file ;
773
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
797
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
774
798
if (filename && !strcmp (filename , "/dev/null" ))
775
799
wcscpy (wfilename , L"nul" );
776
800
else if (!is_valid_win32_path (filename , 1 )) {
777
801
int create = otype && strchr (otype , 'w' );
778
802
errno = create ? EINVAL : ENOENT ;
779
803
return NULL ;
780
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
804
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
781
805
return NULL ;
782
806
783
807
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -827,7 +851,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
827
851
HANDLE h = (HANDLE ) _get_osfhandle (fd );
828
852
if (GetFileType (h ) != FILE_TYPE_PIPE ) {
829
853
if (orig == EINVAL ) {
830
- wchar_t path [MAX_PATH ];
854
+ wchar_t path [MAX_LONG_PATH ];
831
855
DWORD ret = GetFinalPathNameByHandleW (h , path ,
832
856
ARRAY_SIZE (path ), 0 );
833
857
UINT drive_type = ret > 0 && ret < ARRAY_SIZE (path ) ?
@@ -864,27 +888,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
864
888
865
889
int mingw_access (const char * filename , int mode )
866
890
{
867
- wchar_t wfilename [MAX_PATH ];
891
+ wchar_t wfilename [MAX_LONG_PATH ];
868
892
if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
869
893
return 0 ;
870
- if (xutftowcs_path (wfilename , filename ) < 0 )
894
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
871
895
return -1 ;
872
896
/* X_OK is not supported by the MSVCRT version */
873
897
return _waccess (wfilename , mode & ~X_OK );
874
898
}
875
899
900
+ /* cached length of current directory for handle_long_path */
901
+ static int current_directory_len = 0 ;
902
+
876
903
int mingw_chdir (const char * dirname )
877
904
{
878
- wchar_t wdirname [MAX_PATH ];
879
- if (xutftowcs_path (wdirname , dirname ) < 0 )
905
+ int result ;
906
+ wchar_t wdirname [MAX_LONG_PATH ];
907
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
880
908
return -1 ;
881
- return _wchdir (wdirname );
909
+ result = _wchdir (wdirname );
910
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
911
+ return result ;
882
912
}
883
913
884
914
int mingw_chmod (const char * filename , int mode )
885
915
{
886
- wchar_t wfilename [MAX_PATH ];
887
- if (xutftowcs_path (wfilename , filename ) < 0 )
916
+ wchar_t wfilename [MAX_LONG_PATH ];
917
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
888
918
return -1 ;
889
919
return _wchmod (wfilename , mode );
890
920
}
@@ -932,8 +962,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
932
962
static int do_lstat (int follow , const char * file_name , struct stat * buf )
933
963
{
934
964
WIN32_FILE_ATTRIBUTE_DATA fdata ;
935
- wchar_t wfilename [MAX_PATH ];
936
- if (xutftowcs_path (wfilename , file_name ) < 0 )
965
+ wchar_t wfilename [MAX_LONG_PATH ];
966
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
937
967
return -1 ;
938
968
939
969
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -1104,10 +1134,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
1104
1134
FILETIME mft , aft ;
1105
1135
int rc ;
1106
1136
DWORD attrs ;
1107
- wchar_t wfilename [MAX_PATH ];
1137
+ wchar_t wfilename [MAX_LONG_PATH ];
1108
1138
HANDLE osfilehandle ;
1109
1139
1110
- if (xutftowcs_path (wfilename , file_name ) < 0 )
1140
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
1111
1141
return -1 ;
1112
1142
1113
1143
/* must have write permission */
@@ -1190,6 +1220,7 @@ char *mingw_mktemp(char *template)
1190
1220
wchar_t wtemplate [MAX_PATH ];
1191
1221
int offset = 0 ;
1192
1222
1223
+ /* we need to return the path, thus no long paths here! */
1193
1224
if (xutftowcs_path (wtemplate , template ) < 0 )
1194
1225
return NULL ;
1195
1226
@@ -1831,6 +1862,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1831
1862
1832
1863
if (* argv && !strcmp (cmd , * argv ))
1833
1864
wcmd [0 ] = L'\0' ;
1865
+ /*
1866
+ * Paths to executables and to the current directory do not support
1867
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1868
+ */
1834
1869
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1835
1870
return -1 ;
1836
1871
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2520,12 +2555,12 @@ int mingw_rename(const char *pold, const char *pnew)
2520
2555
static int supports_file_rename_info_ex = 1 ;
2521
2556
DWORD attrs , gle ;
2522
2557
int tries = 0 ;
2523
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2558
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2524
2559
int wpnew_len ;
2525
2560
2526
- if (xutftowcs_path (wpold , pold ) < 0 )
2561
+ if (xutftowcs_long_path (wpold , pold ) < 0 )
2527
2562
return -1 ;
2528
- wpnew_len = xutftowcs_path (wpnew , pnew );
2563
+ wpnew_len = xutftowcs_long_path (wpnew , pnew );
2529
2564
if (wpnew_len < 0 )
2530
2565
return -1 ;
2531
2566
@@ -2564,9 +2599,9 @@ int mingw_rename(const char *pold, const char *pnew)
2564
2599
* flex array so that the structure has to be allocated on
2565
2600
* the heap. As we declare this structure ourselves though
2566
2601
* we can avoid the allocation and define FileName to have
2567
- * MAX_PATH bytes.
2602
+ * MAX_LONG_PATH bytes.
2568
2603
*/
2569
- WCHAR FileName [MAX_PATH ];
2604
+ WCHAR FileName [MAX_LONG_PATH ];
2570
2605
} rename_info = { 0 };
2571
2606
HANDLE old_handle = INVALID_HANDLE_VALUE ;
2572
2607
BOOL success ;
@@ -2919,9 +2954,9 @@ int mingw_raise(int sig)
2919
2954
2920
2955
int link (const char * oldpath , const char * newpath )
2921
2956
{
2922
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2923
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2924
- xutftowcs_path (wnewpath , newpath ) < 0 )
2957
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2958
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2959
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2925
2960
return -1 ;
2926
2961
2927
2962
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2989,8 +3024,8 @@ int mingw_is_mount_point(struct strbuf *path)
2989
3024
{
2990
3025
WIN32_FIND_DATAW findbuf = { 0 };
2991
3026
HANDLE handle ;
2992
- wchar_t wfilename [MAX_PATH ];
2993
- int wlen = xutftowcs_path (wfilename , path -> buf );
3027
+ wchar_t wfilename [MAX_LONG_PATH ];
3028
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
2994
3029
if (wlen < 0 )
2995
3030
die (_ ("could not get long path for '%s'" ), path -> buf );
2996
3031
@@ -3142,9 +3177,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
3142
3177
3143
3178
static int is_system32_path (const char * path )
3144
3179
{
3145
- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
3180
+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
3146
3181
3147
- if (xutftowcs_path (wpath , path ) < 0 ||
3182
+ if (xutftowcs_long_path (wpath , path ) < 0 ||
3148
3183
!GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
3149
3184
_wcsicmp (system32 , wpath ))
3150
3185
return 0 ;
@@ -3577,6 +3612,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
3577
3612
}
3578
3613
}
3579
3614
3615
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3616
+ {
3617
+ int result ;
3618
+ wchar_t buf [MAX_LONG_PATH ];
3619
+
3620
+ /*
3621
+ * we don't need special handling if path is relative to the current
3622
+ * directory, and current directory + path don't exceed the desired
3623
+ * max_path limit. This should cover > 99 % of cases with minimal
3624
+ * performance impact (git almost always uses relative paths).
3625
+ */
3626
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3627
+ (current_directory_len + len < max_path ))
3628
+ return len ;
3629
+
3630
+ /*
3631
+ * handle everything else:
3632
+ * - absolute paths: "C:\dir\file"
3633
+ * - absolute UNC paths: "\\server\share\dir\file"
3634
+ * - absolute paths on current drive: "\dir\file"
3635
+ * - relative paths on other drive: "X:file"
3636
+ * - prefixed paths: "\\?\...", "\\.\..."
3637
+ */
3638
+
3639
+ /* convert to absolute path using GetFullPathNameW */
3640
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3641
+ if (!result ) {
3642
+ errno = err_win_to_posix (GetLastError ());
3643
+ return -1 ;
3644
+ }
3645
+
3646
+ /*
3647
+ * return absolute path if it fits within max_path (even if
3648
+ * "cwd + path" doesn't due to '..' components)
3649
+ */
3650
+ if (result < max_path ) {
3651
+ wcscpy (path , buf );
3652
+ return result ;
3653
+ }
3654
+
3655
+ /* error out if we shouldn't expand the path or buf is too small */
3656
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3657
+ errno = ENAMETOOLONG ;
3658
+ return -1 ;
3659
+ }
3660
+
3661
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3662
+ if (buf [0 ] == '\\' ) {
3663
+ /* ...unless already prefixed */
3664
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3665
+ return len ;
3666
+
3667
+ wcscpy (path , L"\\\\?\\UNC\\" );
3668
+ wcscpy (path + 8 , buf + 2 );
3669
+ return result + 6 ;
3670
+ } else {
3671
+ wcscpy (path , L"\\\\?\\" );
3672
+ wcscpy (path + 4 , buf );
3673
+ return result + 4 ;
3674
+ }
3675
+ }
3676
+
3580
3677
#if !defined(_MSC_VER )
3581
3678
/*
3582
3679
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3739,6 +3836,9 @@ int wmain(int argc, const wchar_t **wargv)
3739
3836
/* initialize Unicode console */
3740
3837
winansi_init ();
3741
3838
3839
+ /* init length of current directory for handle_long_path */
3840
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3841
+
3742
3842
/* invoke the real main() using our utf8 version of argv. */
3743
3843
exit_status = main (argc , argv );
3744
3844
0 commit comments