@@ -233,6 +233,7 @@ static int core_restrict_inherited_handles = -1;
233
233
static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY ;
234
234
static char * unset_environment_variables ;
235
235
int core_fscache ;
236
+ int core_long_paths ;
236
237
237
238
int mingw_core_config (const char * var , const char * value , void * cb )
238
239
{
@@ -249,6 +250,11 @@ int mingw_core_config(const char *var, const char *value, void *cb)
249
250
return 0 ;
250
251
}
251
252
253
+ if (!strcmp (var , "core.longpaths" )) {
254
+ core_long_paths = git_config_bool (var , value );
255
+ return 0 ;
256
+ }
257
+
252
258
if (!strcmp (var , "core.unsetenvvars" )) {
253
259
free (unset_environment_variables );
254
260
unset_environment_variables = xstrdup (value );
@@ -295,8 +301,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
295
301
int mingw_unlink (const char * pathname )
296
302
{
297
303
int ret , tries = 0 ;
298
- wchar_t wpathname [MAX_PATH ];
299
- if (xutftowcs_path (wpathname , pathname ) < 0 )
304
+ wchar_t wpathname [MAX_LONG_PATH ];
305
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
300
306
return -1 ;
301
307
302
308
if (DeleteFileW (wpathname ))
@@ -328,7 +334,7 @@ static int is_dir_empty(const wchar_t *wpath)
328
334
{
329
335
WIN32_FIND_DATAW findbuf ;
330
336
HANDLE handle ;
331
- wchar_t wbuf [MAX_PATH + 2 ];
337
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
332
338
wcscpy (wbuf , wpath );
333
339
wcscat (wbuf , L"\\*" );
334
340
handle = FindFirstFileW (wbuf , & findbuf );
@@ -349,7 +355,7 @@ static int is_dir_empty(const wchar_t *wpath)
349
355
int mingw_rmdir (const char * pathname )
350
356
{
351
357
int ret , tries = 0 ;
352
- wchar_t wpathname [MAX_PATH ];
358
+ wchar_t wpathname [MAX_LONG_PATH ];
353
359
struct stat st ;
354
360
355
361
/*
@@ -371,7 +377,7 @@ int mingw_rmdir(const char *pathname)
371
377
return -1 ;
372
378
}
373
379
374
- if (xutftowcs_path (wpathname , pathname ) < 0 )
380
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
375
381
return -1 ;
376
382
377
383
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -450,15 +456,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
450
456
int mingw_mkdir (const char * path , int mode )
451
457
{
452
458
int ret ;
453
- wchar_t wpath [MAX_PATH ];
459
+ wchar_t wpath [MAX_LONG_PATH ];
454
460
455
461
if (!is_valid_win32_path (path , 0 )) {
456
462
errno = EINVAL ;
457
463
return -1 ;
458
464
}
459
465
460
- if (xutftowcs_path (wpath , path ) < 0 )
466
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
467
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
468
+ core_long_paths ) < 0 )
461
469
return -1 ;
470
+
462
471
ret = _wmkdir (wpath );
463
472
if (!ret && needs_hiding (path ))
464
473
return set_hidden_flag (wpath , 1 );
@@ -544,7 +553,7 @@ int mingw_open (const char *filename, int oflags, ...)
544
553
va_list args ;
545
554
unsigned mode ;
546
555
int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
547
- wchar_t wfilename [MAX_PATH ];
556
+ wchar_t wfilename [MAX_LONG_PATH ];
548
557
open_fn_t open_fn ;
549
558
550
559
va_start (args , oflags );
@@ -563,7 +572,7 @@ int mingw_open (const char *filename, int oflags, ...)
563
572
564
573
if (filename && !strcmp (filename , "/dev/null" ))
565
574
wcscpy (wfilename , L"nul" );
566
- else if (xutftowcs_path (wfilename , filename ) < 0 )
575
+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
567
576
return -1 ;
568
577
569
578
fd = open_fn (wfilename , oflags , mode );
@@ -621,14 +630,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
621
630
{
622
631
int hide = needs_hiding (filename );
623
632
FILE * file ;
624
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
633
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
625
634
if (filename && !strcmp (filename , "/dev/null" ))
626
635
wcscpy (wfilename , L"nul" );
627
636
else if (!is_valid_win32_path (filename , 1 )) {
628
637
int create = otype && strchr (otype , 'w' );
629
638
errno = create ? EINVAL : ENOENT ;
630
639
return NULL ;
631
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
640
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
632
641
return NULL ;
633
642
634
643
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -650,14 +659,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
650
659
{
651
660
int hide = needs_hiding (filename );
652
661
FILE * file ;
653
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
662
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
654
663
if (filename && !strcmp (filename , "/dev/null" ))
655
664
wcscpy (wfilename , L"nul" );
656
665
else if (!is_valid_win32_path (filename , 1 )) {
657
666
int create = otype && strchr (otype , 'w' );
658
667
errno = create ? EINVAL : ENOENT ;
659
668
return NULL ;
660
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
669
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
661
670
return NULL ;
662
671
663
672
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -714,27 +723,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
714
723
715
724
int mingw_access (const char * filename , int mode )
716
725
{
717
- wchar_t wfilename [MAX_PATH ];
726
+ wchar_t wfilename [MAX_LONG_PATH ];
718
727
if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
719
728
return 0 ;
720
- if (xutftowcs_path (wfilename , filename ) < 0 )
729
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
721
730
return -1 ;
722
731
/* X_OK is not supported by the MSVCRT version */
723
732
return _waccess (wfilename , mode & ~X_OK );
724
733
}
725
734
735
+ /* cached length of current directory for handle_long_path */
736
+ static int current_directory_len = 0 ;
737
+
726
738
int mingw_chdir (const char * dirname )
727
739
{
728
- wchar_t wdirname [MAX_PATH ];
729
- if (xutftowcs_path (wdirname , dirname ) < 0 )
740
+ int result ;
741
+ wchar_t wdirname [MAX_LONG_PATH ];
742
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
730
743
return -1 ;
731
- return _wchdir (wdirname );
744
+ result = _wchdir (wdirname );
745
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
746
+ return result ;
732
747
}
733
748
734
749
int mingw_chmod (const char * filename , int mode )
735
750
{
736
- wchar_t wfilename [MAX_PATH ];
737
- if (xutftowcs_path (wfilename , filename ) < 0 )
751
+ wchar_t wfilename [MAX_LONG_PATH ];
752
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
738
753
return -1 ;
739
754
return _wchmod (wfilename , mode );
740
755
}
@@ -782,8 +797,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
782
797
static int do_lstat (int follow , const char * file_name , struct stat * buf )
783
798
{
784
799
WIN32_FILE_ATTRIBUTE_DATA fdata ;
785
- wchar_t wfilename [MAX_PATH ];
786
- if (xutftowcs_path (wfilename , file_name ) < 0 )
800
+ wchar_t wfilename [MAX_LONG_PATH ];
801
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
787
802
return -1 ;
788
803
789
804
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -954,10 +969,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
954
969
FILETIME mft , aft ;
955
970
int rc ;
956
971
DWORD attrs ;
957
- wchar_t wfilename [MAX_PATH ];
972
+ wchar_t wfilename [MAX_LONG_PATH ];
958
973
HANDLE osfilehandle ;
959
974
960
- if (xutftowcs_path (wfilename , file_name ) < 0 )
975
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
961
976
return -1 ;
962
977
963
978
/* must have write permission */
@@ -1040,6 +1055,7 @@ char *mingw_mktemp(char *template)
1040
1055
wchar_t wtemplate [MAX_PATH ];
1041
1056
int offset = 0 ;
1042
1057
1058
+ /* we need to return the path, thus no long paths here! */
1043
1059
if (xutftowcs_path (wtemplate , template ) < 0 )
1044
1060
return NULL ;
1045
1061
@@ -1677,6 +1693,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1677
1693
1678
1694
if (* argv && !strcmp (cmd , * argv ))
1679
1695
wcmd [0 ] = L'\0' ;
1696
+ /*
1697
+ * Paths to executables and to the current directory do not support
1698
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1699
+ */
1680
1700
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1681
1701
return -1 ;
1682
1702
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2328,8 +2348,9 @@ int mingw_rename(const char *pold, const char *pnew)
2328
2348
{
2329
2349
DWORD attrs , gle ;
2330
2350
int tries = 0 ;
2331
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2332
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2351
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2352
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2353
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
2333
2354
return -1 ;
2334
2355
2335
2356
/*
@@ -2643,9 +2664,9 @@ int mingw_raise(int sig)
2643
2664
2644
2665
int link (const char * oldpath , const char * newpath )
2645
2666
{
2646
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2647
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2648
- xutftowcs_path (wnewpath , newpath ) < 0 )
2667
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2668
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2669
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2649
2670
return -1 ;
2650
2671
2651
2672
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2713,8 +2734,8 @@ int mingw_is_mount_point(struct strbuf *path)
2713
2734
{
2714
2735
WIN32_FIND_DATAW findbuf = { 0 };
2715
2736
HANDLE handle ;
2716
- wchar_t wfilename [MAX_PATH ];
2717
- int wlen = xutftowcs_path (wfilename , path -> buf );
2737
+ wchar_t wfilename [MAX_LONG_PATH ];
2738
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
2718
2739
if (wlen < 0 )
2719
2740
die (_ ("could not get long path for '%s'" ), path -> buf );
2720
2741
@@ -2859,9 +2880,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
2859
2880
2860
2881
static int is_system32_path (const char * path )
2861
2882
{
2862
- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
2883
+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
2863
2884
2864
- if (xutftowcs_path (wpath , path ) < 0 ||
2885
+ if (xutftowcs_long_path (wpath , path ) < 0 ||
2865
2886
!GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
2866
2887
_wcsicmp (system32 , wpath ))
2867
2888
return 0 ;
@@ -3078,6 +3099,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
3078
3099
}
3079
3100
}
3080
3101
3102
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3103
+ {
3104
+ int result ;
3105
+ wchar_t buf [MAX_LONG_PATH ];
3106
+
3107
+ /*
3108
+ * we don't need special handling if path is relative to the current
3109
+ * directory, and current directory + path don't exceed the desired
3110
+ * max_path limit. This should cover > 99 % of cases with minimal
3111
+ * performance impact (git almost always uses relative paths).
3112
+ */
3113
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3114
+ (current_directory_len + len < max_path ))
3115
+ return len ;
3116
+
3117
+ /*
3118
+ * handle everything else:
3119
+ * - absolute paths: "C:\dir\file"
3120
+ * - absolute UNC paths: "\\server\share\dir\file"
3121
+ * - absolute paths on current drive: "\dir\file"
3122
+ * - relative paths on other drive: "X:file"
3123
+ * - prefixed paths: "\\?\...", "\\.\..."
3124
+ */
3125
+
3126
+ /* convert to absolute path using GetFullPathNameW */
3127
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3128
+ if (!result ) {
3129
+ errno = err_win_to_posix (GetLastError ());
3130
+ return -1 ;
3131
+ }
3132
+
3133
+ /*
3134
+ * return absolute path if it fits within max_path (even if
3135
+ * "cwd + path" doesn't due to '..' components)
3136
+ */
3137
+ if (result < max_path ) {
3138
+ wcscpy (path , buf );
3139
+ return result ;
3140
+ }
3141
+
3142
+ /* error out if we shouldn't expand the path or buf is too small */
3143
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3144
+ errno = ENAMETOOLONG ;
3145
+ return -1 ;
3146
+ }
3147
+
3148
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3149
+ if (buf [0 ] == '\\' ) {
3150
+ /* ...unless already prefixed */
3151
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3152
+ return len ;
3153
+
3154
+ wcscpy (path , L"\\\\?\\UNC\\" );
3155
+ wcscpy (path + 8 , buf + 2 );
3156
+ return result + 6 ;
3157
+ } else {
3158
+ wcscpy (path , L"\\\\?\\" );
3159
+ wcscpy (path + 4 , buf );
3160
+ return result + 4 ;
3161
+ }
3162
+ }
3163
+
3081
3164
#if !defined(_MSC_VER )
3082
3165
/*
3083
3166
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3239,6 +3322,9 @@ int wmain(int argc, const wchar_t **wargv)
3239
3322
/* initialize Unicode console */
3240
3323
winansi_init ();
3241
3324
3325
+ /* init length of current directory for handle_long_path */
3326
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3327
+
3242
3328
/* invoke the real main() using our utf8 version of argv. */
3243
3329
exit_status = main (argc , argv );
3244
3330
0 commit comments