@@ -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 );
@@ -628,7 +652,7 @@ int mingw_open (const char *filename, int oflags, ...)
628
652
va_list args ;
629
653
unsigned mode ;
630
654
int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
631
- wchar_t wfilename [MAX_PATH ];
655
+ wchar_t wfilename [MAX_LONG_PATH ];
632
656
open_fn_t open_fn ;
633
657
634
658
va_start (args , oflags );
@@ -658,7 +682,7 @@ int mingw_open (const char *filename, int oflags, ...)
658
682
659
683
if (filename && !strcmp (filename , "/dev/null" ))
660
684
wcscpy (wfilename , L"nul" );
661
- else if (xutftowcs_path (wfilename , filename ) < 0 )
685
+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
662
686
return -1 ;
663
687
664
688
fd = open_fn (wfilename , oflags , mode );
@@ -716,14 +740,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
716
740
{
717
741
int hide = needs_hiding (filename );
718
742
FILE * file ;
719
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
743
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
720
744
if (filename && !strcmp (filename , "/dev/null" ))
721
745
wcscpy (wfilename , L"nul" );
722
746
else if (!is_valid_win32_path (filename , 1 )) {
723
747
int create = otype && strchr (otype , 'w' );
724
748
errno = create ? EINVAL : ENOENT ;
725
749
return NULL ;
726
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
750
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
727
751
return NULL ;
728
752
729
753
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -745,14 +769,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
745
769
{
746
770
int hide = needs_hiding (filename );
747
771
FILE * file ;
748
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
772
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
749
773
if (filename && !strcmp (filename , "/dev/null" ))
750
774
wcscpy (wfilename , L"nul" );
751
775
else if (!is_valid_win32_path (filename , 1 )) {
752
776
int create = otype && strchr (otype , 'w' );
753
777
errno = create ? EINVAL : ENOENT ;
754
778
return NULL ;
755
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
779
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
756
780
return NULL ;
757
781
758
782
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -802,7 +826,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
802
826
HANDLE h = (HANDLE ) _get_osfhandle (fd );
803
827
if (GetFileType (h ) != FILE_TYPE_PIPE ) {
804
828
if (orig == EINVAL ) {
805
- wchar_t path [MAX_PATH ];
829
+ wchar_t path [MAX_LONG_PATH ];
806
830
DWORD ret = GetFinalPathNameByHandleW (h , path ,
807
831
ARRAY_SIZE (path ), 0 );
808
832
UINT drive_type = ret > 0 && ret < ARRAY_SIZE (path ) ?
@@ -839,27 +863,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
839
863
840
864
int mingw_access (const char * filename , int mode )
841
865
{
842
- wchar_t wfilename [MAX_PATH ];
866
+ wchar_t wfilename [MAX_LONG_PATH ];
843
867
if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
844
868
return 0 ;
845
- if (xutftowcs_path (wfilename , filename ) < 0 )
869
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
846
870
return -1 ;
847
871
/* X_OK is not supported by the MSVCRT version */
848
872
return _waccess (wfilename , mode & ~X_OK );
849
873
}
850
874
875
+ /* cached length of current directory for handle_long_path */
876
+ static int current_directory_len = 0 ;
877
+
851
878
int mingw_chdir (const char * dirname )
852
879
{
853
- wchar_t wdirname [MAX_PATH ];
854
- if (xutftowcs_path (wdirname , dirname ) < 0 )
880
+ int result ;
881
+ wchar_t wdirname [MAX_LONG_PATH ];
882
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
855
883
return -1 ;
856
- return _wchdir (wdirname );
884
+ result = _wchdir (wdirname );
885
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
886
+ return result ;
857
887
}
858
888
859
889
int mingw_chmod (const char * filename , int mode )
860
890
{
861
- wchar_t wfilename [MAX_PATH ];
862
- if (xutftowcs_path (wfilename , filename ) < 0 )
891
+ wchar_t wfilename [MAX_LONG_PATH ];
892
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
863
893
return -1 ;
864
894
return _wchmod (wfilename , mode );
865
895
}
@@ -907,8 +937,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
907
937
static int do_lstat (int follow , const char * file_name , struct stat * buf )
908
938
{
909
939
WIN32_FILE_ATTRIBUTE_DATA fdata ;
910
- wchar_t wfilename [MAX_PATH ];
911
- if (xutftowcs_path (wfilename , file_name ) < 0 )
940
+ wchar_t wfilename [MAX_LONG_PATH ];
941
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
912
942
return -1 ;
913
943
914
944
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -1079,10 +1109,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
1079
1109
FILETIME mft , aft ;
1080
1110
int rc ;
1081
1111
DWORD attrs ;
1082
- wchar_t wfilename [MAX_PATH ];
1112
+ wchar_t wfilename [MAX_LONG_PATH ];
1083
1113
HANDLE osfilehandle ;
1084
1114
1085
- if (xutftowcs_path (wfilename , file_name ) < 0 )
1115
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
1086
1116
return -1 ;
1087
1117
1088
1118
/* must have write permission */
@@ -1165,6 +1195,7 @@ char *mingw_mktemp(char *template)
1165
1195
wchar_t wtemplate [MAX_PATH ];
1166
1196
int offset = 0 ;
1167
1197
1198
+ /* we need to return the path, thus no long paths here! */
1168
1199
if (xutftowcs_path (wtemplate , template ) < 0 )
1169
1200
return NULL ;
1170
1201
@@ -1817,6 +1848,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1817
1848
1818
1849
if (* argv && !strcmp (cmd , * argv ))
1819
1850
wcmd [0 ] = L'\0' ;
1851
+ /*
1852
+ * Paths to executables and to the current directory do not support
1853
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1854
+ */
1820
1855
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1821
1856
return -1 ;
1822
1857
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2545,12 +2580,12 @@ int mingw_rename(const char *pold, const char *pnew)
2545
2580
static int supports_file_rename_info_ex = 1 ;
2546
2581
DWORD attrs , gle ;
2547
2582
int tries = 0 ;
2548
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2583
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2549
2584
int wpnew_len ;
2550
2585
2551
- if (xutftowcs_path (wpold , pold ) < 0 )
2586
+ if (xutftowcs_long_path (wpold , pold ) < 0 )
2552
2587
return -1 ;
2553
- wpnew_len = xutftowcs_path (wpnew , pnew );
2588
+ wpnew_len = xutftowcs_long_path (wpnew , pnew );
2554
2589
if (wpnew_len < 0 )
2555
2590
return -1 ;
2556
2591
@@ -2942,9 +2977,9 @@ int mingw_raise(int sig)
2942
2977
2943
2978
int link (const char * oldpath , const char * newpath )
2944
2979
{
2945
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2946
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2947
- xutftowcs_path (wnewpath , newpath ) < 0 )
2980
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2981
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2982
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2948
2983
return -1 ;
2949
2984
2950
2985
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -3012,8 +3047,8 @@ int mingw_is_mount_point(struct strbuf *path)
3012
3047
{
3013
3048
WIN32_FIND_DATAW findbuf = { 0 };
3014
3049
HANDLE handle ;
3015
- wchar_t wfilename [MAX_PATH ];
3016
- int wlen = xutftowcs_path (wfilename , path -> buf );
3050
+ wchar_t wfilename [MAX_LONG_PATH ];
3051
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
3017
3052
if (wlen < 0 )
3018
3053
die (_ ("could not get long path for '%s'" ), path -> buf );
3019
3054
@@ -3158,9 +3193,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
3158
3193
3159
3194
static int is_system32_path (const char * path )
3160
3195
{
3161
- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
3196
+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
3162
3197
3163
- if (xutftowcs_path (wpath , path ) < 0 ||
3198
+ if (xutftowcs_long_path (wpath , path ) < 0 ||
3164
3199
!GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
3165
3200
_wcsicmp (system32 , wpath ))
3166
3201
return 0 ;
@@ -3572,6 +3607,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
3572
3607
}
3573
3608
}
3574
3609
3610
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3611
+ {
3612
+ int result ;
3613
+ wchar_t buf [MAX_LONG_PATH ];
3614
+
3615
+ /*
3616
+ * we don't need special handling if path is relative to the current
3617
+ * directory, and current directory + path don't exceed the desired
3618
+ * max_path limit. This should cover > 99 % of cases with minimal
3619
+ * performance impact (git almost always uses relative paths).
3620
+ */
3621
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3622
+ (current_directory_len + len < max_path ))
3623
+ return len ;
3624
+
3625
+ /*
3626
+ * handle everything else:
3627
+ * - absolute paths: "C:\dir\file"
3628
+ * - absolute UNC paths: "\\server\share\dir\file"
3629
+ * - absolute paths on current drive: "\dir\file"
3630
+ * - relative paths on other drive: "X:file"
3631
+ * - prefixed paths: "\\?\...", "\\.\..."
3632
+ */
3633
+
3634
+ /* convert to absolute path using GetFullPathNameW */
3635
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3636
+ if (!result ) {
3637
+ errno = err_win_to_posix (GetLastError ());
3638
+ return -1 ;
3639
+ }
3640
+
3641
+ /*
3642
+ * return absolute path if it fits within max_path (even if
3643
+ * "cwd + path" doesn't due to '..' components)
3644
+ */
3645
+ if (result < max_path ) {
3646
+ wcscpy (path , buf );
3647
+ return result ;
3648
+ }
3649
+
3650
+ /* error out if we shouldn't expand the path or buf is too small */
3651
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3652
+ errno = ENAMETOOLONG ;
3653
+ return -1 ;
3654
+ }
3655
+
3656
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3657
+ if (buf [0 ] == '\\' ) {
3658
+ /* ...unless already prefixed */
3659
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3660
+ return len ;
3661
+
3662
+ wcscpy (path , L"\\\\?\\UNC\\" );
3663
+ wcscpy (path + 8 , buf + 2 );
3664
+ return result + 6 ;
3665
+ } else {
3666
+ wcscpy (path , L"\\\\?\\" );
3667
+ wcscpy (path + 4 , buf );
3668
+ return result + 4 ;
3669
+ }
3670
+ }
3671
+
3575
3672
#if !defined(_MSC_VER )
3576
3673
/*
3577
3674
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3734,6 +3831,9 @@ int wmain(int argc, const wchar_t **wargv)
3734
3831
/* initialize Unicode console */
3735
3832
winansi_init ();
3736
3833
3834
+ /* init length of current directory for handle_long_path */
3835
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3836
+
3737
3837
/* invoke the real main() using our utf8 version of argv. */
3738
3838
exit_status = main (argc , argv );
3739
3839
0 commit comments