@@ -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
@@ -1654,6 +1670,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1654
1670
1655
1671
if (* argv && !strcmp (cmd , * argv ))
1656
1672
wcmd [0 ] = L'\0' ;
1673
+ /*
1674
+ * Paths to executables and to the current directory do not support
1675
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1676
+ */
1657
1677
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1658
1678
return -1 ;
1659
1679
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2305,8 +2325,9 @@ int mingw_rename(const char *pold, const char *pnew)
2305
2325
{
2306
2326
DWORD attrs , gle ;
2307
2327
int tries = 0 ;
2308
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2309
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2328
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2329
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2330
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
2310
2331
return -1 ;
2311
2332
2312
2333
/*
@@ -2620,9 +2641,9 @@ int mingw_raise(int sig)
2620
2641
2621
2642
int link (const char * oldpath , const char * newpath )
2622
2643
{
2623
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2624
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2625
- xutftowcs_path (wnewpath , newpath ) < 0 )
2644
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2645
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2646
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2626
2647
return -1 ;
2627
2648
2628
2649
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2690,8 +2711,8 @@ int mingw_is_mount_point(struct strbuf *path)
2690
2711
{
2691
2712
WIN32_FIND_DATAW findbuf = { 0 };
2692
2713
HANDLE handle ;
2693
- wchar_t wfilename [MAX_PATH ];
2694
- int wlen = xutftowcs_path (wfilename , path -> buf );
2714
+ wchar_t wfilename [MAX_LONG_PATH ];
2715
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
2695
2716
if (wlen < 0 )
2696
2717
die (_ ("could not get long path for '%s'" ), path -> buf );
2697
2718
@@ -3055,6 +3076,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
3055
3076
}
3056
3077
}
3057
3078
3079
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3080
+ {
3081
+ int result ;
3082
+ wchar_t buf [MAX_LONG_PATH ];
3083
+
3084
+ /*
3085
+ * we don't need special handling if path is relative to the current
3086
+ * directory, and current directory + path don't exceed the desired
3087
+ * max_path limit. This should cover > 99 % of cases with minimal
3088
+ * performance impact (git almost always uses relative paths).
3089
+ */
3090
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3091
+ (current_directory_len + len < max_path ))
3092
+ return len ;
3093
+
3094
+ /*
3095
+ * handle everything else:
3096
+ * - absolute paths: "C:\dir\file"
3097
+ * - absolute UNC paths: "\\server\share\dir\file"
3098
+ * - absolute paths on current drive: "\dir\file"
3099
+ * - relative paths on other drive: "X:file"
3100
+ * - prefixed paths: "\\?\...", "\\.\..."
3101
+ */
3102
+
3103
+ /* convert to absolute path using GetFullPathNameW */
3104
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3105
+ if (!result ) {
3106
+ errno = err_win_to_posix (GetLastError ());
3107
+ return -1 ;
3108
+ }
3109
+
3110
+ /*
3111
+ * return absolute path if it fits within max_path (even if
3112
+ * "cwd + path" doesn't due to '..' components)
3113
+ */
3114
+ if (result < max_path ) {
3115
+ wcscpy (path , buf );
3116
+ return result ;
3117
+ }
3118
+
3119
+ /* error out if we shouldn't expand the path or buf is too small */
3120
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3121
+ errno = ENAMETOOLONG ;
3122
+ return -1 ;
3123
+ }
3124
+
3125
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3126
+ if (buf [0 ] == '\\' ) {
3127
+ /* ...unless already prefixed */
3128
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3129
+ return len ;
3130
+
3131
+ wcscpy (path , L"\\\\?\\UNC\\" );
3132
+ wcscpy (path + 8 , buf + 2 );
3133
+ return result + 6 ;
3134
+ } else {
3135
+ wcscpy (path , L"\\\\?\\" );
3136
+ wcscpy (path + 4 , buf );
3137
+ return result + 4 ;
3138
+ }
3139
+ }
3140
+
3058
3141
#if !defined(_MSC_VER )
3059
3142
/*
3060
3143
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3216,6 +3299,9 @@ int wmain(int argc, const wchar_t **wargv)
3216
3299
/* initialize Unicode console */
3217
3300
winansi_init ();
3218
3301
3302
+ /* init length of current directory for handle_long_path */
3303
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3304
+
3219
3305
/* invoke the real main() using our utf8 version of argv. */
3220
3306
exit_status = main (argc , argv );
3221
3307
0 commit comments