@@ -251,6 +251,27 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
251
251
static char * unset_environment_variables ;
252
252
int core_fscache ;
253
253
254
+ int are_long_paths_enabled (void )
255
+ {
256
+ /* default to `false` during initialization */
257
+ static const int fallback = 0 ;
258
+
259
+ static int enabled = -1 ;
260
+
261
+ if (enabled < 0 ) {
262
+ /* avoid infinite recursion */
263
+ if (!the_repository )
264
+ return fallback ;
265
+
266
+ if (the_repository -> config &&
267
+ the_repository -> config -> hash_initialized &&
268
+ git_config_get_bool ("core.longpaths" , & enabled ) < 0 )
269
+ enabled = 0 ;
270
+ }
271
+
272
+ return enabled < 0 ? fallback : enabled ;
273
+ }
274
+
254
275
int mingw_core_config (const char * var , const char * value ,
255
276
const struct config_context * ctx UNUSED ,
256
277
void * cb UNUSED )
@@ -307,8 +328,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
307
328
int mingw_unlink (const char * pathname , int handle_in_use_error )
308
329
{
309
330
int ret , tries = 0 ;
310
- wchar_t wpathname [MAX_PATH ];
311
- if (xutftowcs_path (wpathname , pathname ) < 0 )
331
+ wchar_t wpathname [MAX_LONG_PATH ];
332
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
312
333
return -1 ;
313
334
314
335
if (DeleteFileW (wpathname ))
@@ -343,7 +364,7 @@ static int is_dir_empty(const wchar_t *wpath)
343
364
{
344
365
WIN32_FIND_DATAW findbuf ;
345
366
HANDLE handle ;
346
- wchar_t wbuf [MAX_PATH + 2 ];
367
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
347
368
wcscpy (wbuf , wpath );
348
369
wcscat (wbuf , L"\\*" );
349
370
handle = FindFirstFileW (wbuf , & findbuf );
@@ -364,7 +385,7 @@ static int is_dir_empty(const wchar_t *wpath)
364
385
int mingw_rmdir (const char * pathname )
365
386
{
366
387
int ret , tries = 0 ;
367
- wchar_t wpathname [MAX_PATH ];
388
+ wchar_t wpathname [MAX_LONG_PATH ];
368
389
struct stat st ;
369
390
370
391
/*
@@ -386,7 +407,7 @@ int mingw_rmdir(const char *pathname)
386
407
return -1 ;
387
408
}
388
409
389
- if (xutftowcs_path (wpathname , pathname ) < 0 )
410
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
390
411
return -1 ;
391
412
392
413
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -465,15 +486,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
465
486
int mingw_mkdir (const char * path , int mode UNUSED )
466
487
{
467
488
int ret ;
468
- wchar_t wpath [MAX_PATH ];
489
+ wchar_t wpath [MAX_LONG_PATH ];
469
490
470
491
if (!is_valid_win32_path (path , 0 )) {
471
492
errno = EINVAL ;
472
493
return -1 ;
473
494
}
474
495
475
- if (xutftowcs_path (wpath , path ) < 0 )
496
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
497
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
498
+ are_long_paths_enabled ()) < 0 )
476
499
return -1 ;
500
+
477
501
ret = _wmkdir (wpath );
478
502
if (!ret && needs_hiding (path ))
479
503
return set_hidden_flag (wpath , 1 );
@@ -637,7 +661,7 @@ int mingw_open (const char *filename, int oflags, ...)
637
661
va_list args ;
638
662
unsigned mode ;
639
663
int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
640
- wchar_t wfilename [MAX_PATH ];
664
+ wchar_t wfilename [MAX_LONG_PATH ];
641
665
open_fn_t open_fn ;
642
666
643
667
DECLARE_PROC_ADDR (ntdll .dll , NTSTATUS , NTAPI , RtlGetLastNtStatus , void );
@@ -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 );
@@ -742,14 +766,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
742
766
{
743
767
int hide = needs_hiding (filename );
744
768
FILE * file ;
745
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
769
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
746
770
if (filename && !strcmp (filename , "/dev/null" ))
747
771
wcscpy (wfilename , L"nul" );
748
772
else if (!is_valid_win32_path (filename , 1 )) {
749
773
int create = otype && strchr (otype , 'w' );
750
774
errno = create ? EINVAL : ENOENT ;
751
775
return NULL ;
752
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
776
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
753
777
return NULL ;
754
778
755
779
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -771,14 +795,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
771
795
{
772
796
int hide = needs_hiding (filename );
773
797
FILE * file ;
774
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
798
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
775
799
if (filename && !strcmp (filename , "/dev/null" ))
776
800
wcscpy (wfilename , L"nul" );
777
801
else if (!is_valid_win32_path (filename , 1 )) {
778
802
int create = otype && strchr (otype , 'w' );
779
803
errno = create ? EINVAL : ENOENT ;
780
804
return NULL ;
781
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
805
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
782
806
return NULL ;
783
807
784
808
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -828,7 +852,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
828
852
HANDLE h = (HANDLE ) _get_osfhandle (fd );
829
853
if (GetFileType (h ) != FILE_TYPE_PIPE ) {
830
854
if (orig == EINVAL ) {
831
- wchar_t path [MAX_PATH ];
855
+ wchar_t path [MAX_LONG_PATH ];
832
856
DWORD ret = GetFinalPathNameByHandleW (h , path ,
833
857
ARRAY_SIZE (path ), 0 );
834
858
UINT drive_type = ret > 0 && ret < ARRAY_SIZE (path ) ?
@@ -865,27 +889,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
865
889
866
890
int mingw_access (const char * filename , int mode )
867
891
{
868
- wchar_t wfilename [MAX_PATH ];
892
+ wchar_t wfilename [MAX_LONG_PATH ];
869
893
if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
870
894
return 0 ;
871
- if (xutftowcs_path (wfilename , filename ) < 0 )
895
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
872
896
return -1 ;
873
897
/* X_OK is not supported by the MSVCRT version */
874
898
return _waccess (wfilename , mode & ~X_OK );
875
899
}
876
900
901
+ /* cached length of current directory for handle_long_path */
902
+ static int current_directory_len = 0 ;
903
+
877
904
int mingw_chdir (const char * dirname )
878
905
{
879
- wchar_t wdirname [MAX_PATH ];
880
- if (xutftowcs_path (wdirname , dirname ) < 0 )
906
+ int result ;
907
+ wchar_t wdirname [MAX_LONG_PATH ];
908
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
881
909
return -1 ;
882
- return _wchdir (wdirname );
910
+ result = _wchdir (wdirname );
911
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
912
+ return result ;
883
913
}
884
914
885
915
int mingw_chmod (const char * filename , int mode )
886
916
{
887
- wchar_t wfilename [MAX_PATH ];
888
- if (xutftowcs_path (wfilename , filename ) < 0 )
917
+ wchar_t wfilename [MAX_LONG_PATH ];
918
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
889
919
return -1 ;
890
920
return _wchmod (wfilename , mode );
891
921
}
@@ -933,8 +963,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
933
963
static int do_lstat (int follow , const char * file_name , struct stat * buf )
934
964
{
935
965
WIN32_FILE_ATTRIBUTE_DATA fdata ;
936
- wchar_t wfilename [MAX_PATH ];
937
- if (xutftowcs_path (wfilename , file_name ) < 0 )
966
+ wchar_t wfilename [MAX_LONG_PATH ];
967
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
938
968
return -1 ;
939
969
940
970
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -1105,10 +1135,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
1105
1135
FILETIME mft , aft ;
1106
1136
int rc ;
1107
1137
DWORD attrs ;
1108
- wchar_t wfilename [MAX_PATH ];
1138
+ wchar_t wfilename [MAX_LONG_PATH ];
1109
1139
HANDLE osfilehandle ;
1110
1140
1111
- if (xutftowcs_path (wfilename , file_name ) < 0 )
1141
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
1112
1142
return -1 ;
1113
1143
1114
1144
/* must have write permission */
@@ -1191,6 +1221,7 @@ char *mingw_mktemp(char *template)
1191
1221
wchar_t wtemplate [MAX_PATH ];
1192
1222
int offset = 0 ;
1193
1223
1224
+ /* we need to return the path, thus no long paths here! */
1194
1225
if (xutftowcs_path (wtemplate , template ) < 0 )
1195
1226
return NULL ;
1196
1227
@@ -1832,6 +1863,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1832
1863
1833
1864
if (* argv && !strcmp (cmd , * argv ))
1834
1865
wcmd [0 ] = L'\0' ;
1866
+ /*
1867
+ * Paths to executables and to the current directory do not support
1868
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1869
+ */
1835
1870
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1836
1871
return -1 ;
1837
1872
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2521,12 +2556,12 @@ int mingw_rename(const char *pold, const char *pnew)
2521
2556
static int supports_file_rename_info_ex = 1 ;
2522
2557
DWORD attrs , gle ;
2523
2558
int tries = 0 ;
2524
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2559
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2525
2560
int wpnew_len ;
2526
2561
2527
- if (xutftowcs_path (wpold , pold ) < 0 )
2562
+ if (xutftowcs_long_path (wpold , pold ) < 0 )
2528
2563
return -1 ;
2529
- wpnew_len = xutftowcs_path (wpnew , pnew );
2564
+ wpnew_len = xutftowcs_long_path (wpnew , pnew );
2530
2565
if (wpnew_len < 0 )
2531
2566
return -1 ;
2532
2567
@@ -2565,9 +2600,9 @@ int mingw_rename(const char *pold, const char *pnew)
2565
2600
* flex array so that the structure has to be allocated on
2566
2601
* the heap. As we declare this structure ourselves though
2567
2602
* we can avoid the allocation and define FileName to have
2568
- * MAX_PATH bytes.
2603
+ * MAX_LONG_PATH bytes.
2569
2604
*/
2570
- WCHAR FileName [MAX_PATH ];
2605
+ WCHAR FileName [MAX_LONG_PATH ];
2571
2606
} rename_info = { 0 };
2572
2607
HANDLE old_handle = INVALID_HANDLE_VALUE ;
2573
2608
BOOL success ;
@@ -2926,9 +2961,9 @@ int mingw_raise(int sig)
2926
2961
2927
2962
int link (const char * oldpath , const char * newpath )
2928
2963
{
2929
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2930
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2931
- xutftowcs_path (wnewpath , newpath ) < 0 )
2964
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2965
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2966
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2932
2967
return -1 ;
2933
2968
2934
2969
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2996,8 +3031,8 @@ int mingw_is_mount_point(struct strbuf *path)
2996
3031
{
2997
3032
WIN32_FIND_DATAW findbuf = { 0 };
2998
3033
HANDLE handle ;
2999
- wchar_t wfilename [MAX_PATH ];
3000
- int wlen = xutftowcs_path (wfilename , path -> buf );
3034
+ wchar_t wfilename [MAX_LONG_PATH ];
3035
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
3001
3036
if (wlen < 0 )
3002
3037
die (_ ("could not get long path for '%s'" ), path -> buf );
3003
3038
@@ -3149,9 +3184,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
3149
3184
3150
3185
static int is_system32_path (const char * path )
3151
3186
{
3152
- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
3187
+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
3153
3188
3154
- if (xutftowcs_path (wpath , path ) < 0 ||
3189
+ if (xutftowcs_long_path (wpath , path ) < 0 ||
3155
3190
!GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
3156
3191
_wcsicmp (system32 , wpath ))
3157
3192
return 0 ;
@@ -3584,6 +3619,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
3584
3619
}
3585
3620
}
3586
3621
3622
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3623
+ {
3624
+ int result ;
3625
+ wchar_t buf [MAX_LONG_PATH ];
3626
+
3627
+ /*
3628
+ * we don't need special handling if path is relative to the current
3629
+ * directory, and current directory + path don't exceed the desired
3630
+ * max_path limit. This should cover > 99 % of cases with minimal
3631
+ * performance impact (git almost always uses relative paths).
3632
+ */
3633
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3634
+ (current_directory_len + len < max_path ))
3635
+ return len ;
3636
+
3637
+ /*
3638
+ * handle everything else:
3639
+ * - absolute paths: "C:\dir\file"
3640
+ * - absolute UNC paths: "\\server\share\dir\file"
3641
+ * - absolute paths on current drive: "\dir\file"
3642
+ * - relative paths on other drive: "X:file"
3643
+ * - prefixed paths: "\\?\...", "\\.\..."
3644
+ */
3645
+
3646
+ /* convert to absolute path using GetFullPathNameW */
3647
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3648
+ if (!result ) {
3649
+ errno = err_win_to_posix (GetLastError ());
3650
+ return -1 ;
3651
+ }
3652
+
3653
+ /*
3654
+ * return absolute path if it fits within max_path (even if
3655
+ * "cwd + path" doesn't due to '..' components)
3656
+ */
3657
+ if (result < max_path ) {
3658
+ wcscpy (path , buf );
3659
+ return result ;
3660
+ }
3661
+
3662
+ /* error out if we shouldn't expand the path or buf is too small */
3663
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3664
+ errno = ENAMETOOLONG ;
3665
+ return -1 ;
3666
+ }
3667
+
3668
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3669
+ if (buf [0 ] == '\\' ) {
3670
+ /* ...unless already prefixed */
3671
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3672
+ return len ;
3673
+
3674
+ wcscpy (path , L"\\\\?\\UNC\\" );
3675
+ wcscpy (path + 8 , buf + 2 );
3676
+ return result + 6 ;
3677
+ } else {
3678
+ wcscpy (path , L"\\\\?\\" );
3679
+ wcscpy (path + 4 , buf );
3680
+ return result + 4 ;
3681
+ }
3682
+ }
3683
+
3587
3684
#if !defined(_MSC_VER )
3588
3685
/*
3589
3686
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3746,6 +3843,9 @@ int wmain(int argc, const wchar_t **wargv)
3746
3843
/* initialize Unicode console */
3747
3844
winansi_init ();
3748
3845
3846
+ /* init length of current directory for handle_long_path */
3847
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3848
+
3749
3849
/* invoke the real main() using our utf8 version of argv. */
3750
3850
exit_status = main (argc , argv );
3751
3851
0 commit comments