@@ -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 )
@@ -304,8 +325,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
304
325
int mingw_unlink (const char * pathname )
305
326
{
306
327
int ret , tries = 0 ;
307
- wchar_t wpathname [MAX_PATH ];
308
- if (xutftowcs_path (wpathname , pathname ) < 0 )
328
+ wchar_t wpathname [MAX_LONG_PATH ];
329
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
309
330
return -1 ;
310
331
311
332
if (DeleteFileW (wpathname ))
@@ -337,7 +358,7 @@ static int is_dir_empty(const wchar_t *wpath)
337
358
{
338
359
WIN32_FIND_DATAW findbuf ;
339
360
HANDLE handle ;
340
- wchar_t wbuf [MAX_PATH + 2 ];
361
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
341
362
wcscpy (wbuf , wpath );
342
363
wcscat (wbuf , L"\\*" );
343
364
handle = FindFirstFileW (wbuf , & findbuf );
@@ -358,7 +379,7 @@ static int is_dir_empty(const wchar_t *wpath)
358
379
int mingw_rmdir (const char * pathname )
359
380
{
360
381
int ret , tries = 0 ;
361
- wchar_t wpathname [MAX_PATH ];
382
+ wchar_t wpathname [MAX_LONG_PATH ];
362
383
struct stat st ;
363
384
364
385
/*
@@ -380,7 +401,7 @@ int mingw_rmdir(const char *pathname)
380
401
return -1 ;
381
402
}
382
403
383
- if (xutftowcs_path (wpathname , pathname ) < 0 )
404
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
384
405
return -1 ;
385
406
386
407
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -459,15 +480,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
459
480
int mingw_mkdir (const char * path , int mode UNUSED )
460
481
{
461
482
int ret ;
462
- wchar_t wpath [MAX_PATH ];
483
+ wchar_t wpath [MAX_LONG_PATH ];
463
484
464
485
if (!is_valid_win32_path (path , 0 )) {
465
486
errno = EINVAL ;
466
487
return -1 ;
467
488
}
468
489
469
- if (xutftowcs_path (wpath , path ) < 0 )
490
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
491
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
492
+ are_long_paths_enabled ()) < 0 )
470
493
return -1 ;
494
+
471
495
ret = _wmkdir (wpath );
472
496
if (!ret && needs_hiding (path ))
473
497
return set_hidden_flag (wpath , 1 );
@@ -629,7 +653,7 @@ int mingw_open (const char *filename, int oflags, ...)
629
653
va_list args ;
630
654
unsigned mode ;
631
655
int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
632
- wchar_t wfilename [MAX_PATH ];
656
+ wchar_t wfilename [MAX_LONG_PATH ];
633
657
open_fn_t open_fn ;
634
658
635
659
va_start (args , oflags );
@@ -659,7 +683,7 @@ int mingw_open (const char *filename, int oflags, ...)
659
683
660
684
if (filename && !strcmp (filename , "/dev/null" ))
661
685
wcscpy (wfilename , L"nul" );
662
- else if (xutftowcs_path (wfilename , filename ) < 0 )
686
+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
663
687
return -1 ;
664
688
665
689
fd = open_fn (wfilename , oflags , mode );
@@ -717,14 +741,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
717
741
{
718
742
int hide = needs_hiding (filename );
719
743
FILE * file ;
720
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
744
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
721
745
if (filename && !strcmp (filename , "/dev/null" ))
722
746
wcscpy (wfilename , L"nul" );
723
747
else if (!is_valid_win32_path (filename , 1 )) {
724
748
int create = otype && strchr (otype , 'w' );
725
749
errno = create ? EINVAL : ENOENT ;
726
750
return NULL ;
727
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
751
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
728
752
return NULL ;
729
753
730
754
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -746,14 +770,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
746
770
{
747
771
int hide = needs_hiding (filename );
748
772
FILE * file ;
749
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
773
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
750
774
if (filename && !strcmp (filename , "/dev/null" ))
751
775
wcscpy (wfilename , L"nul" );
752
776
else if (!is_valid_win32_path (filename , 1 )) {
753
777
int create = otype && strchr (otype , 'w' );
754
778
errno = create ? EINVAL : ENOENT ;
755
779
return NULL ;
756
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
780
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
757
781
return NULL ;
758
782
759
783
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -803,7 +827,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
803
827
HANDLE h = (HANDLE ) _get_osfhandle (fd );
804
828
if (GetFileType (h ) != FILE_TYPE_PIPE ) {
805
829
if (orig == EINVAL ) {
806
- wchar_t path [MAX_PATH ];
830
+ wchar_t path [MAX_LONG_PATH ];
807
831
DWORD ret = GetFinalPathNameByHandleW (h , path ,
808
832
ARRAY_SIZE (path ), 0 );
809
833
UINT drive_type = ret > 0 && ret < ARRAY_SIZE (path ) ?
@@ -840,27 +864,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
840
864
841
865
int mingw_access (const char * filename , int mode )
842
866
{
843
- wchar_t wfilename [MAX_PATH ];
867
+ wchar_t wfilename [MAX_LONG_PATH ];
844
868
if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
845
869
return 0 ;
846
- if (xutftowcs_path (wfilename , filename ) < 0 )
870
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
847
871
return -1 ;
848
872
/* X_OK is not supported by the MSVCRT version */
849
873
return _waccess (wfilename , mode & ~X_OK );
850
874
}
851
875
876
+ /* cached length of current directory for handle_long_path */
877
+ static int current_directory_len = 0 ;
878
+
852
879
int mingw_chdir (const char * dirname )
853
880
{
854
- wchar_t wdirname [MAX_PATH ];
855
- if (xutftowcs_path (wdirname , dirname ) < 0 )
881
+ int result ;
882
+ wchar_t wdirname [MAX_LONG_PATH ];
883
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
856
884
return -1 ;
857
- return _wchdir (wdirname );
885
+ result = _wchdir (wdirname );
886
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
887
+ return result ;
858
888
}
859
889
860
890
int mingw_chmod (const char * filename , int mode )
861
891
{
862
- wchar_t wfilename [MAX_PATH ];
863
- if (xutftowcs_path (wfilename , filename ) < 0 )
892
+ wchar_t wfilename [MAX_LONG_PATH ];
893
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
864
894
return -1 ;
865
895
return _wchmod (wfilename , mode );
866
896
}
@@ -908,8 +938,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
908
938
static int do_lstat (int follow , const char * file_name , struct stat * buf )
909
939
{
910
940
WIN32_FILE_ATTRIBUTE_DATA fdata ;
911
- wchar_t wfilename [MAX_PATH ];
912
- if (xutftowcs_path (wfilename , file_name ) < 0 )
941
+ wchar_t wfilename [MAX_LONG_PATH ];
942
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
913
943
return -1 ;
914
944
915
945
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -1080,10 +1110,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
1080
1110
FILETIME mft , aft ;
1081
1111
int rc ;
1082
1112
DWORD attrs ;
1083
- wchar_t wfilename [MAX_PATH ];
1113
+ wchar_t wfilename [MAX_LONG_PATH ];
1084
1114
HANDLE osfilehandle ;
1085
1115
1086
- if (xutftowcs_path (wfilename , file_name ) < 0 )
1116
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
1087
1117
return -1 ;
1088
1118
1089
1119
/* must have write permission */
@@ -1166,6 +1196,7 @@ char *mingw_mktemp(char *template)
1166
1196
wchar_t wtemplate [MAX_PATH ];
1167
1197
int offset = 0 ;
1168
1198
1199
+ /* we need to return the path, thus no long paths here! */
1169
1200
if (xutftowcs_path (wtemplate , template ) < 0 )
1170
1201
return NULL ;
1171
1202
@@ -1807,6 +1838,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1807
1838
1808
1839
if (* argv && !strcmp (cmd , * argv ))
1809
1840
wcmd [0 ] = L'\0' ;
1841
+ /*
1842
+ * Paths to executables and to the current directory do not support
1843
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1844
+ */
1810
1845
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1811
1846
return -1 ;
1812
1847
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2496,12 +2531,12 @@ int mingw_rename(const char *pold, const char *pnew)
2496
2531
static int supports_file_rename_info_ex = 1 ;
2497
2532
DWORD attrs , gle ;
2498
2533
int tries = 0 ;
2499
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2534
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2500
2535
int wpnew_len ;
2501
2536
2502
- if (xutftowcs_path (wpold , pold ) < 0 )
2537
+ if (xutftowcs_long_path (wpold , pold ) < 0 )
2503
2538
return -1 ;
2504
- wpnew_len = xutftowcs_path (wpnew , pnew );
2539
+ wpnew_len = xutftowcs_long_path (wpnew , pnew );
2505
2540
if (wpnew_len < 0 )
2506
2541
return -1 ;
2507
2542
@@ -2895,9 +2930,9 @@ int mingw_raise(int sig)
2895
2930
2896
2931
int link (const char * oldpath , const char * newpath )
2897
2932
{
2898
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2899
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2900
- xutftowcs_path (wnewpath , newpath ) < 0 )
2933
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2934
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2935
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2901
2936
return -1 ;
2902
2937
2903
2938
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2965,8 +3000,8 @@ int mingw_is_mount_point(struct strbuf *path)
2965
3000
{
2966
3001
WIN32_FIND_DATAW findbuf = { 0 };
2967
3002
HANDLE handle ;
2968
- wchar_t wfilename [MAX_PATH ];
2969
- int wlen = xutftowcs_path (wfilename , path -> buf );
3003
+ wchar_t wfilename [MAX_LONG_PATH ];
3004
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
2970
3005
if (wlen < 0 )
2971
3006
die (_ ("could not get long path for '%s'" ), path -> buf );
2972
3007
@@ -3114,9 +3149,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
3114
3149
3115
3150
static int is_system32_path (const char * path )
3116
3151
{
3117
- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
3152
+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
3118
3153
3119
- if (xutftowcs_path (wpath , path ) < 0 ||
3154
+ if (xutftowcs_long_path (wpath , path ) < 0 ||
3120
3155
!GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
3121
3156
_wcsicmp (system32 , wpath ))
3122
3157
return 0 ;
@@ -3528,6 +3563,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
3528
3563
}
3529
3564
}
3530
3565
3566
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3567
+ {
3568
+ int result ;
3569
+ wchar_t buf [MAX_LONG_PATH ];
3570
+
3571
+ /*
3572
+ * we don't need special handling if path is relative to the current
3573
+ * directory, and current directory + path don't exceed the desired
3574
+ * max_path limit. This should cover > 99 % of cases with minimal
3575
+ * performance impact (git almost always uses relative paths).
3576
+ */
3577
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3578
+ (current_directory_len + len < max_path ))
3579
+ return len ;
3580
+
3581
+ /*
3582
+ * handle everything else:
3583
+ * - absolute paths: "C:\dir\file"
3584
+ * - absolute UNC paths: "\\server\share\dir\file"
3585
+ * - absolute paths on current drive: "\dir\file"
3586
+ * - relative paths on other drive: "X:file"
3587
+ * - prefixed paths: "\\?\...", "\\.\..."
3588
+ */
3589
+
3590
+ /* convert to absolute path using GetFullPathNameW */
3591
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3592
+ if (!result ) {
3593
+ errno = err_win_to_posix (GetLastError ());
3594
+ return -1 ;
3595
+ }
3596
+
3597
+ /*
3598
+ * return absolute path if it fits within max_path (even if
3599
+ * "cwd + path" doesn't due to '..' components)
3600
+ */
3601
+ if (result < max_path ) {
3602
+ wcscpy (path , buf );
3603
+ return result ;
3604
+ }
3605
+
3606
+ /* error out if we shouldn't expand the path or buf is too small */
3607
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3608
+ errno = ENAMETOOLONG ;
3609
+ return -1 ;
3610
+ }
3611
+
3612
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3613
+ if (buf [0 ] == '\\' ) {
3614
+ /* ...unless already prefixed */
3615
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3616
+ return len ;
3617
+
3618
+ wcscpy (path , L"\\\\?\\UNC\\" );
3619
+ wcscpy (path + 8 , buf + 2 );
3620
+ return result + 6 ;
3621
+ } else {
3622
+ wcscpy (path , L"\\\\?\\" );
3623
+ wcscpy (path + 4 , buf );
3624
+ return result + 4 ;
3625
+ }
3626
+ }
3627
+
3531
3628
#if !defined(_MSC_VER )
3532
3629
/*
3533
3630
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3690,6 +3787,9 @@ int wmain(int argc, const wchar_t **wargv)
3690
3787
/* initialize Unicode console */
3691
3788
winansi_init ();
3692
3789
3790
+ /* init length of current directory for handle_long_path */
3791
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3792
+
3693
3793
/* invoke the real main() using our utf8 version of argv. */
3694
3794
exit_status = main (argc , argv );
3695
3795
0 commit comments