@@ -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 )
@@ -2469,12 +2504,12 @@ int mingw_rename(const char *pold, const char *pnew)
2469
2504
static int supports_file_rename_info_ex = 1 ;
2470
2505
DWORD attrs , gle ;
2471
2506
int tries = 0 ;
2472
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2507
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2473
2508
int wpnew_len ;
2474
2509
2475
- if (xutftowcs_path (wpold , pold ) < 0 )
2510
+ if (xutftowcs_long_path (wpold , pold ) < 0 )
2476
2511
return -1 ;
2477
- wpnew_len = xutftowcs_path (wpnew , pnew );
2512
+ wpnew_len = xutftowcs_long_path (wpnew , pnew );
2478
2513
if (wpnew_len < 0 )
2479
2514
return -1 ;
2480
2515
@@ -2866,9 +2901,9 @@ int mingw_raise(int sig)
2866
2901
2867
2902
int link (const char * oldpath , const char * newpath )
2868
2903
{
2869
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2870
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2871
- xutftowcs_path (wnewpath , newpath ) < 0 )
2904
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2905
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2906
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2872
2907
return -1 ;
2873
2908
2874
2909
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2936,8 +2971,8 @@ int mingw_is_mount_point(struct strbuf *path)
2936
2971
{
2937
2972
WIN32_FIND_DATAW findbuf = { 0 };
2938
2973
HANDLE handle ;
2939
- wchar_t wfilename [MAX_PATH ];
2940
- int wlen = xutftowcs_path (wfilename , path -> buf );
2974
+ wchar_t wfilename [MAX_LONG_PATH ];
2975
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
2941
2976
if (wlen < 0 )
2942
2977
die (_ ("could not get long path for '%s'" ), path -> buf );
2943
2978
@@ -3082,9 +3117,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
3082
3117
3083
3118
static int is_system32_path (const char * path )
3084
3119
{
3085
- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
3120
+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
3086
3121
3087
- if (xutftowcs_path (wpath , path ) < 0 ||
3122
+ if (xutftowcs_long_path (wpath , path ) < 0 ||
3088
3123
!GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
3089
3124
_wcsicmp (system32 , wpath ))
3090
3125
return 0 ;
@@ -3496,6 +3531,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
3496
3531
}
3497
3532
}
3498
3533
3534
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3535
+ {
3536
+ int result ;
3537
+ wchar_t buf [MAX_LONG_PATH ];
3538
+
3539
+ /*
3540
+ * we don't need special handling if path is relative to the current
3541
+ * directory, and current directory + path don't exceed the desired
3542
+ * max_path limit. This should cover > 99 % of cases with minimal
3543
+ * performance impact (git almost always uses relative paths).
3544
+ */
3545
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3546
+ (current_directory_len + len < max_path ))
3547
+ return len ;
3548
+
3549
+ /*
3550
+ * handle everything else:
3551
+ * - absolute paths: "C:\dir\file"
3552
+ * - absolute UNC paths: "\\server\share\dir\file"
3553
+ * - absolute paths on current drive: "\dir\file"
3554
+ * - relative paths on other drive: "X:file"
3555
+ * - prefixed paths: "\\?\...", "\\.\..."
3556
+ */
3557
+
3558
+ /* convert to absolute path using GetFullPathNameW */
3559
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3560
+ if (!result ) {
3561
+ errno = err_win_to_posix (GetLastError ());
3562
+ return -1 ;
3563
+ }
3564
+
3565
+ /*
3566
+ * return absolute path if it fits within max_path (even if
3567
+ * "cwd + path" doesn't due to '..' components)
3568
+ */
3569
+ if (result < max_path ) {
3570
+ wcscpy (path , buf );
3571
+ return result ;
3572
+ }
3573
+
3574
+ /* error out if we shouldn't expand the path or buf is too small */
3575
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3576
+ errno = ENAMETOOLONG ;
3577
+ return -1 ;
3578
+ }
3579
+
3580
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3581
+ if (buf [0 ] == '\\' ) {
3582
+ /* ...unless already prefixed */
3583
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3584
+ return len ;
3585
+
3586
+ wcscpy (path , L"\\\\?\\UNC\\" );
3587
+ wcscpy (path + 8 , buf + 2 );
3588
+ return result + 6 ;
3589
+ } else {
3590
+ wcscpy (path , L"\\\\?\\" );
3591
+ wcscpy (path + 4 , buf );
3592
+ return result + 4 ;
3593
+ }
3594
+ }
3595
+
3499
3596
#if !defined(_MSC_VER )
3500
3597
/*
3501
3598
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3658,6 +3755,9 @@ int wmain(int argc, const wchar_t **wargv)
3658
3755
/* initialize Unicode console */
3659
3756
winansi_init ();
3660
3757
3758
+ /* init length of current directory for handle_long_path */
3759
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3760
+
3661
3761
/* invoke the real main() using our utf8 version of argv. */
3662
3762
exit_status = main (argc , argv );
3663
3763
0 commit comments