@@ -231,6 +231,7 @@ static int core_restrict_inherited_handles = -1;
231
231
static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY ;
232
232
static char * unset_environment_variables ;
233
233
int core_fscache ;
234
+ int core_long_paths ;
234
235
235
236
int mingw_core_config (const char * var , const char * value , void * cb )
236
237
{
@@ -247,6 +248,11 @@ int mingw_core_config(const char *var, const char *value, void *cb)
247
248
return 0 ;
248
249
}
249
250
251
+ if (!strcmp (var , "core.longpaths" )) {
252
+ core_long_paths = git_config_bool (var , value );
253
+ return 0 ;
254
+ }
255
+
250
256
if (!strcmp (var , "core.unsetenvvars" )) {
251
257
free (unset_environment_variables );
252
258
unset_environment_variables = xstrdup (value );
@@ -293,8 +299,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
293
299
int mingw_unlink (const char * pathname )
294
300
{
295
301
int ret , tries = 0 ;
296
- wchar_t wpathname [MAX_PATH ];
297
- if (xutftowcs_path (wpathname , pathname ) < 0 )
302
+ wchar_t wpathname [MAX_LONG_PATH ];
303
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
298
304
return -1 ;
299
305
300
306
if (DeleteFileW (wpathname ))
@@ -326,7 +332,7 @@ static int is_dir_empty(const wchar_t *wpath)
326
332
{
327
333
WIN32_FIND_DATAW findbuf ;
328
334
HANDLE handle ;
329
- wchar_t wbuf [MAX_PATH + 2 ];
335
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
330
336
wcscpy (wbuf , wpath );
331
337
wcscat (wbuf , L"\\*" );
332
338
handle = FindFirstFileW (wbuf , & findbuf );
@@ -347,7 +353,7 @@ static int is_dir_empty(const wchar_t *wpath)
347
353
int mingw_rmdir (const char * pathname )
348
354
{
349
355
int ret , tries = 0 ;
350
- wchar_t wpathname [MAX_PATH ];
356
+ wchar_t wpathname [MAX_LONG_PATH ];
351
357
struct stat st ;
352
358
353
359
/*
@@ -369,7 +375,7 @@ int mingw_rmdir(const char *pathname)
369
375
return -1 ;
370
376
}
371
377
372
- if (xutftowcs_path (wpathname , pathname ) < 0 )
378
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
373
379
return -1 ;
374
380
375
381
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -448,15 +454,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
448
454
int mingw_mkdir (const char * path , int mode )
449
455
{
450
456
int ret ;
451
- wchar_t wpath [MAX_PATH ];
457
+ wchar_t wpath [MAX_LONG_PATH ];
452
458
453
459
if (!is_valid_win32_path (path , 0 )) {
454
460
errno = EINVAL ;
455
461
return -1 ;
456
462
}
457
463
458
- if (xutftowcs_path (wpath , path ) < 0 )
464
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
465
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
466
+ core_long_paths ) < 0 )
459
467
return -1 ;
468
+
460
469
ret = _wmkdir (wpath );
461
470
if (!ret && needs_hiding (path ))
462
471
return set_hidden_flag (wpath , 1 );
@@ -542,7 +551,7 @@ int mingw_open (const char *filename, int oflags, ...)
542
551
va_list args ;
543
552
unsigned mode ;
544
553
int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
545
- wchar_t wfilename [MAX_PATH ];
554
+ wchar_t wfilename [MAX_LONG_PATH ];
546
555
open_fn_t open_fn ;
547
556
548
557
va_start (args , oflags );
@@ -561,7 +570,7 @@ int mingw_open (const char *filename, int oflags, ...)
561
570
562
571
if (filename && !strcmp (filename , "/dev/null" ))
563
572
wcscpy (wfilename , L"nul" );
564
- else if (xutftowcs_path (wfilename , filename ) < 0 )
573
+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
565
574
return -1 ;
566
575
567
576
fd = open_fn (wfilename , oflags , mode );
@@ -619,14 +628,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
619
628
{
620
629
int hide = needs_hiding (filename );
621
630
FILE * file ;
622
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
631
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
623
632
if (filename && !strcmp (filename , "/dev/null" ))
624
633
wcscpy (wfilename , L"nul" );
625
634
else if (!is_valid_win32_path (filename , 1 )) {
626
635
int create = otype && strchr (otype , 'w' );
627
636
errno = create ? EINVAL : ENOENT ;
628
637
return NULL ;
629
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
638
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
630
639
return NULL ;
631
640
632
641
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -648,14 +657,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
648
657
{
649
658
int hide = needs_hiding (filename );
650
659
FILE * file ;
651
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
660
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
652
661
if (filename && !strcmp (filename , "/dev/null" ))
653
662
wcscpy (wfilename , L"nul" );
654
663
else if (!is_valid_win32_path (filename , 1 )) {
655
664
int create = otype && strchr (otype , 'w' );
656
665
errno = create ? EINVAL : ENOENT ;
657
666
return NULL ;
658
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
667
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
659
668
return NULL ;
660
669
661
670
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -712,27 +721,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
712
721
713
722
int mingw_access (const char * filename , int mode )
714
723
{
715
- wchar_t wfilename [MAX_PATH ];
724
+ wchar_t wfilename [MAX_LONG_PATH ];
716
725
if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
717
726
return 0 ;
718
- if (xutftowcs_path (wfilename , filename ) < 0 )
727
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
719
728
return -1 ;
720
729
/* X_OK is not supported by the MSVCRT version */
721
730
return _waccess (wfilename , mode & ~X_OK );
722
731
}
723
732
733
+ /* cached length of current directory for handle_long_path */
734
+ static int current_directory_len = 0 ;
735
+
724
736
int mingw_chdir (const char * dirname )
725
737
{
726
- wchar_t wdirname [MAX_PATH ];
727
- if (xutftowcs_path (wdirname , dirname ) < 0 )
738
+ int result ;
739
+ wchar_t wdirname [MAX_LONG_PATH ];
740
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
728
741
return -1 ;
729
- return _wchdir (wdirname );
742
+ result = _wchdir (wdirname );
743
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
744
+ return result ;
730
745
}
731
746
732
747
int mingw_chmod (const char * filename , int mode )
733
748
{
734
- wchar_t wfilename [MAX_PATH ];
735
- if (xutftowcs_path (wfilename , filename ) < 0 )
749
+ wchar_t wfilename [MAX_LONG_PATH ];
750
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
736
751
return -1 ;
737
752
return _wchmod (wfilename , mode );
738
753
}
@@ -780,8 +795,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
780
795
static int do_lstat (int follow , const char * file_name , struct stat * buf )
781
796
{
782
797
WIN32_FILE_ATTRIBUTE_DATA fdata ;
783
- wchar_t wfilename [MAX_PATH ];
784
- if (xutftowcs_path (wfilename , file_name ) < 0 )
798
+ wchar_t wfilename [MAX_LONG_PATH ];
799
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
785
800
return -1 ;
786
801
787
802
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -952,8 +967,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
952
967
FILETIME mft , aft ;
953
968
int fh , rc ;
954
969
DWORD attrs ;
955
- wchar_t wfilename [MAX_PATH ];
956
- if (xutftowcs_path (wfilename , file_name ) < 0 )
970
+ wchar_t wfilename [MAX_LONG_PATH ];
971
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
957
972
return -1 ;
958
973
959
974
/* must have write permission */
@@ -1023,6 +1038,7 @@ char *mingw_mktemp(char *template)
1023
1038
wchar_t wtemplate [MAX_PATH ];
1024
1039
int offset = 0 ;
1025
1040
1041
+ /* we need to return the path, thus no long paths here! */
1026
1042
if (xutftowcs_path (wtemplate , template ) < 0 )
1027
1043
return NULL ;
1028
1044
@@ -1656,6 +1672,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1656
1672
1657
1673
if (* argv && !strcmp (cmd , * argv ))
1658
1674
wcmd [0 ] = L'\0' ;
1675
+ /*
1676
+ * Paths to executables and to the current directory do not support
1677
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1678
+ */
1659
1679
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1660
1680
return -1 ;
1661
1681
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2307,8 +2327,9 @@ int mingw_rename(const char *pold, const char *pnew)
2307
2327
{
2308
2328
DWORD attrs , gle ;
2309
2329
int tries = 0 ;
2310
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2311
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2330
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2331
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2332
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
2312
2333
return -1 ;
2313
2334
2314
2335
/*
@@ -2622,9 +2643,9 @@ int mingw_raise(int sig)
2622
2643
2623
2644
int link (const char * oldpath , const char * newpath )
2624
2645
{
2625
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2626
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2627
- xutftowcs_path (wnewpath , newpath ) < 0 )
2646
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2647
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2648
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2628
2649
return -1 ;
2629
2650
2630
2651
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2692,8 +2713,8 @@ int mingw_is_mount_point(struct strbuf *path)
2692
2713
{
2693
2714
WIN32_FIND_DATAW findbuf = { 0 };
2694
2715
HANDLE handle ;
2695
- wchar_t wfilename [MAX_PATH ];
2696
- int wlen = xutftowcs_path (wfilename , path -> buf );
2716
+ wchar_t wfilename [MAX_LONG_PATH ];
2717
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
2697
2718
if (wlen < 0 )
2698
2719
die (_ ("could not get long path for '%s'" ), path -> buf );
2699
2720
@@ -2838,9 +2859,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
2838
2859
2839
2860
static int is_system32_path (const char * path )
2840
2861
{
2841
- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
2862
+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
2842
2863
2843
- if (xutftowcs_path (wpath , path ) < 0 ||
2864
+ if (xutftowcs_long_path (wpath , path ) < 0 ||
2844
2865
!GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
2845
2866
_wcsicmp (system32 , wpath ))
2846
2867
return 0 ;
@@ -3057,6 +3078,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
3057
3078
}
3058
3079
}
3059
3080
3081
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3082
+ {
3083
+ int result ;
3084
+ wchar_t buf [MAX_LONG_PATH ];
3085
+
3086
+ /*
3087
+ * we don't need special handling if path is relative to the current
3088
+ * directory, and current directory + path don't exceed the desired
3089
+ * max_path limit. This should cover > 99 % of cases with minimal
3090
+ * performance impact (git almost always uses relative paths).
3091
+ */
3092
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3093
+ (current_directory_len + len < max_path ))
3094
+ return len ;
3095
+
3096
+ /*
3097
+ * handle everything else:
3098
+ * - absolute paths: "C:\dir\file"
3099
+ * - absolute UNC paths: "\\server\share\dir\file"
3100
+ * - absolute paths on current drive: "\dir\file"
3101
+ * - relative paths on other drive: "X:file"
3102
+ * - prefixed paths: "\\?\...", "\\.\..."
3103
+ */
3104
+
3105
+ /* convert to absolute path using GetFullPathNameW */
3106
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3107
+ if (!result ) {
3108
+ errno = err_win_to_posix (GetLastError ());
3109
+ return -1 ;
3110
+ }
3111
+
3112
+ /*
3113
+ * return absolute path if it fits within max_path (even if
3114
+ * "cwd + path" doesn't due to '..' components)
3115
+ */
3116
+ if (result < max_path ) {
3117
+ wcscpy (path , buf );
3118
+ return result ;
3119
+ }
3120
+
3121
+ /* error out if we shouldn't expand the path or buf is too small */
3122
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3123
+ errno = ENAMETOOLONG ;
3124
+ return -1 ;
3125
+ }
3126
+
3127
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3128
+ if (buf [0 ] == '\\' ) {
3129
+ /* ...unless already prefixed */
3130
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3131
+ return len ;
3132
+
3133
+ wcscpy (path , L"\\\\?\\UNC\\" );
3134
+ wcscpy (path + 8 , buf + 2 );
3135
+ return result + 6 ;
3136
+ } else {
3137
+ wcscpy (path , L"\\\\?\\" );
3138
+ wcscpy (path + 4 , buf );
3139
+ return result + 4 ;
3140
+ }
3141
+ }
3142
+
3060
3143
#if !defined(_MSC_VER )
3061
3144
/*
3062
3145
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3218,6 +3301,9 @@ int wmain(int argc, const wchar_t **wargv)
3218
3301
/* initialize Unicode console */
3219
3302
winansi_init ();
3220
3303
3304
+ /* init length of current directory for handle_long_path */
3305
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3306
+
3221
3307
/* invoke the real main() using our utf8 version of argv. */
3222
3308
exit_status = main (argc , argv );
3223
3309
0 commit comments