@@ -234,6 +234,7 @@ static int core_restrict_inherited_handles = -1;
234
234
static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY ;
235
235
static char * unset_environment_variables ;
236
236
int core_fscache ;
237
+ int core_long_paths ;
237
238
238
239
int mingw_core_config (const char * var , const char * value , void * cb )
239
240
{
@@ -250,6 +251,11 @@ int mingw_core_config(const char *var, const char *value, void *cb)
250
251
return 0 ;
251
252
}
252
253
254
+ if (!strcmp (var , "core.longpaths" )) {
255
+ core_long_paths = git_config_bool (var , value );
256
+ return 0 ;
257
+ }
258
+
253
259
if (!strcmp (var , "core.unsetenvvars" )) {
254
260
free (unset_environment_variables );
255
261
unset_environment_variables = xstrdup (value );
@@ -296,8 +302,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
296
302
int mingw_unlink (const char * pathname )
297
303
{
298
304
int ret , tries = 0 ;
299
- wchar_t wpathname [MAX_PATH ];
300
- if (xutftowcs_path (wpathname , pathname ) < 0 )
305
+ wchar_t wpathname [MAX_LONG_PATH ];
306
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
301
307
return -1 ;
302
308
303
309
if (DeleteFileW (wpathname ))
@@ -329,7 +335,7 @@ static int is_dir_empty(const wchar_t *wpath)
329
335
{
330
336
WIN32_FIND_DATAW findbuf ;
331
337
HANDLE handle ;
332
- wchar_t wbuf [MAX_PATH + 2 ];
338
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
333
339
wcscpy (wbuf , wpath );
334
340
wcscat (wbuf , L"\\*" );
335
341
handle = FindFirstFileW (wbuf , & findbuf );
@@ -350,7 +356,7 @@ static int is_dir_empty(const wchar_t *wpath)
350
356
int mingw_rmdir (const char * pathname )
351
357
{
352
358
int ret , tries = 0 ;
353
- wchar_t wpathname [MAX_PATH ];
359
+ wchar_t wpathname [MAX_LONG_PATH ];
354
360
struct stat st ;
355
361
356
362
/*
@@ -372,7 +378,7 @@ int mingw_rmdir(const char *pathname)
372
378
return -1 ;
373
379
}
374
380
375
- if (xutftowcs_path (wpathname , pathname ) < 0 )
381
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
376
382
return -1 ;
377
383
378
384
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -451,15 +457,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
451
457
int mingw_mkdir (const char * path , int mode )
452
458
{
453
459
int ret ;
454
- wchar_t wpath [MAX_PATH ];
460
+ wchar_t wpath [MAX_LONG_PATH ];
455
461
456
462
if (!is_valid_win32_path (path , 0 )) {
457
463
errno = EINVAL ;
458
464
return -1 ;
459
465
}
460
466
461
- if (xutftowcs_path (wpath , path ) < 0 )
467
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
468
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
469
+ core_long_paths ) < 0 )
462
470
return -1 ;
471
+
463
472
ret = _wmkdir (wpath );
464
473
if (!ret && needs_hiding (path ))
465
474
return set_hidden_flag (wpath , 1 );
@@ -545,7 +554,7 @@ int mingw_open (const char *filename, int oflags, ...)
545
554
va_list args ;
546
555
unsigned mode ;
547
556
int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
548
- wchar_t wfilename [MAX_PATH ];
557
+ wchar_t wfilename [MAX_LONG_PATH ];
549
558
open_fn_t open_fn ;
550
559
551
560
va_start (args , oflags );
@@ -564,7 +573,7 @@ int mingw_open (const char *filename, int oflags, ...)
564
573
565
574
if (filename && !strcmp (filename , "/dev/null" ))
566
575
wcscpy (wfilename , L"nul" );
567
- else if (xutftowcs_path (wfilename , filename ) < 0 )
576
+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
568
577
return -1 ;
569
578
570
579
fd = open_fn (wfilename , oflags , mode );
@@ -622,14 +631,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
622
631
{
623
632
int hide = needs_hiding (filename );
624
633
FILE * file ;
625
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
634
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
626
635
if (filename && !strcmp (filename , "/dev/null" ))
627
636
wcscpy (wfilename , L"nul" );
628
637
else if (!is_valid_win32_path (filename , 1 )) {
629
638
int create = otype && strchr (otype , 'w' );
630
639
errno = create ? EINVAL : ENOENT ;
631
640
return NULL ;
632
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
641
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
633
642
return NULL ;
634
643
635
644
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -651,14 +660,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
651
660
{
652
661
int hide = needs_hiding (filename );
653
662
FILE * file ;
654
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
663
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
655
664
if (filename && !strcmp (filename , "/dev/null" ))
656
665
wcscpy (wfilename , L"nul" );
657
666
else if (!is_valid_win32_path (filename , 1 )) {
658
667
int create = otype && strchr (otype , 'w' );
659
668
errno = create ? EINVAL : ENOENT ;
660
669
return NULL ;
661
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
670
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
662
671
return NULL ;
663
672
664
673
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -715,27 +724,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
715
724
716
725
int mingw_access (const char * filename , int mode )
717
726
{
718
- wchar_t wfilename [MAX_PATH ];
727
+ wchar_t wfilename [MAX_LONG_PATH ];
719
728
if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
720
729
return 0 ;
721
- if (xutftowcs_path (wfilename , filename ) < 0 )
730
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
722
731
return -1 ;
723
732
/* X_OK is not supported by the MSVCRT version */
724
733
return _waccess (wfilename , mode & ~X_OK );
725
734
}
726
735
736
+ /* cached length of current directory for handle_long_path */
737
+ static int current_directory_len = 0 ;
738
+
727
739
int mingw_chdir (const char * dirname )
728
740
{
729
- wchar_t wdirname [MAX_PATH ];
730
- if (xutftowcs_path (wdirname , dirname ) < 0 )
741
+ int result ;
742
+ wchar_t wdirname [MAX_LONG_PATH ];
743
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
731
744
return -1 ;
732
- return _wchdir (wdirname );
745
+ result = _wchdir (wdirname );
746
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
747
+ return result ;
733
748
}
734
749
735
750
int mingw_chmod (const char * filename , int mode )
736
751
{
737
- wchar_t wfilename [MAX_PATH ];
738
- if (xutftowcs_path (wfilename , filename ) < 0 )
752
+ wchar_t wfilename [MAX_LONG_PATH ];
753
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
739
754
return -1 ;
740
755
return _wchmod (wfilename , mode );
741
756
}
@@ -783,8 +798,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
783
798
static int do_lstat (int follow , const char * file_name , struct stat * buf )
784
799
{
785
800
WIN32_FILE_ATTRIBUTE_DATA fdata ;
786
- wchar_t wfilename [MAX_PATH ];
787
- if (xutftowcs_path (wfilename , file_name ) < 0 )
801
+ wchar_t wfilename [MAX_LONG_PATH ];
802
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
788
803
return -1 ;
789
804
790
805
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -955,8 +970,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
955
970
FILETIME mft , aft ;
956
971
int fh , rc ;
957
972
DWORD attrs ;
958
- wchar_t wfilename [MAX_PATH ];
959
- if (xutftowcs_path (wfilename , file_name ) < 0 )
973
+ wchar_t wfilename [MAX_LONG_PATH ];
974
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
960
975
return -1 ;
961
976
962
977
/* must have write permission */
@@ -1026,6 +1041,7 @@ char *mingw_mktemp(char *template)
1026
1041
wchar_t wtemplate [MAX_PATH ];
1027
1042
int offset = 0 ;
1028
1043
1044
+ /* we need to return the path, thus no long paths here! */
1029
1045
if (xutftowcs_path (wtemplate , template ) < 0 )
1030
1046
return NULL ;
1031
1047
@@ -1663,6 +1679,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1663
1679
1664
1680
if (* argv && !strcmp (cmd , * argv ))
1665
1681
wcmd [0 ] = L'\0' ;
1682
+ /*
1683
+ * Paths to executables and to the current directory do not support
1684
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1685
+ */
1666
1686
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1667
1687
return -1 ;
1668
1688
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2314,8 +2334,9 @@ int mingw_rename(const char *pold, const char *pnew)
2314
2334
{
2315
2335
DWORD attrs , gle ;
2316
2336
int tries = 0 ;
2317
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2318
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2337
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2338
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2339
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
2319
2340
return -1 ;
2320
2341
2321
2342
/*
@@ -2629,9 +2650,9 @@ int mingw_raise(int sig)
2629
2650
2630
2651
int link (const char * oldpath , const char * newpath )
2631
2652
{
2632
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2633
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2634
- xutftowcs_path (wnewpath , newpath ) < 0 )
2653
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2654
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2655
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2635
2656
return -1 ;
2636
2657
2637
2658
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2699,8 +2720,8 @@ int mingw_is_mount_point(struct strbuf *path)
2699
2720
{
2700
2721
WIN32_FIND_DATAW findbuf = { 0 };
2701
2722
HANDLE handle ;
2702
- wchar_t wfilename [MAX_PATH ];
2703
- int wlen = xutftowcs_path (wfilename , path -> buf );
2723
+ wchar_t wfilename [MAX_LONG_PATH ];
2724
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
2704
2725
if (wlen < 0 )
2705
2726
die (_ ("could not get long path for '%s'" ), path -> buf );
2706
2727
@@ -2845,9 +2866,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
2845
2866
2846
2867
static int is_system32_path (const char * path )
2847
2868
{
2848
- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
2869
+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
2849
2870
2850
- if (xutftowcs_path (wpath , path ) < 0 ||
2871
+ if (xutftowcs_long_path (wpath , path ) < 0 ||
2851
2872
!GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
2852
2873
_wcsicmp (system32 , wpath ))
2853
2874
return 0 ;
@@ -3150,6 +3171,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
3150
3171
}
3151
3172
}
3152
3173
3174
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3175
+ {
3176
+ int result ;
3177
+ wchar_t buf [MAX_LONG_PATH ];
3178
+
3179
+ /*
3180
+ * we don't need special handling if path is relative to the current
3181
+ * directory, and current directory + path don't exceed the desired
3182
+ * max_path limit. This should cover > 99 % of cases with minimal
3183
+ * performance impact (git almost always uses relative paths).
3184
+ */
3185
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3186
+ (current_directory_len + len < max_path ))
3187
+ return len ;
3188
+
3189
+ /*
3190
+ * handle everything else:
3191
+ * - absolute paths: "C:\dir\file"
3192
+ * - absolute UNC paths: "\\server\share\dir\file"
3193
+ * - absolute paths on current drive: "\dir\file"
3194
+ * - relative paths on other drive: "X:file"
3195
+ * - prefixed paths: "\\?\...", "\\.\..."
3196
+ */
3197
+
3198
+ /* convert to absolute path using GetFullPathNameW */
3199
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3200
+ if (!result ) {
3201
+ errno = err_win_to_posix (GetLastError ());
3202
+ return -1 ;
3203
+ }
3204
+
3205
+ /*
3206
+ * return absolute path if it fits within max_path (even if
3207
+ * "cwd + path" doesn't due to '..' components)
3208
+ */
3209
+ if (result < max_path ) {
3210
+ wcscpy (path , buf );
3211
+ return result ;
3212
+ }
3213
+
3214
+ /* error out if we shouldn't expand the path or buf is too small */
3215
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3216
+ errno = ENAMETOOLONG ;
3217
+ return -1 ;
3218
+ }
3219
+
3220
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3221
+ if (buf [0 ] == '\\' ) {
3222
+ /* ...unless already prefixed */
3223
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3224
+ return len ;
3225
+
3226
+ wcscpy (path , L"\\\\?\\UNC\\" );
3227
+ wcscpy (path + 8 , buf + 2 );
3228
+ return result + 6 ;
3229
+ } else {
3230
+ wcscpy (path , L"\\\\?\\" );
3231
+ wcscpy (path + 4 , buf );
3232
+ return result + 4 ;
3233
+ }
3234
+ }
3235
+
3153
3236
#if !defined(_MSC_VER )
3154
3237
/*
3155
3238
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3311,6 +3394,9 @@ int wmain(int argc, const wchar_t **wargv)
3311
3394
/* initialize Unicode console */
3312
3395
winansi_init ();
3313
3396
3397
+ /* init length of current directory for handle_long_path */
3398
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3399
+
3314
3400
/* invoke the real main() using our utf8 version of argv. */
3315
3401
exit_status = main (argc , argv );
3316
3402
0 commit comments