@@ -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
/* read-only files cannot be removed */
@@ -323,7 +329,7 @@ static int is_dir_empty(const wchar_t *wpath)
323
329
{
324
330
WIN32_FIND_DATAW findbuf ;
325
331
HANDLE handle ;
326
- wchar_t wbuf [MAX_PATH + 2 ];
332
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
327
333
wcscpy (wbuf , wpath );
328
334
wcscat (wbuf , L"\\*" );
329
335
handle = FindFirstFileW (wbuf , & findbuf );
@@ -344,8 +350,8 @@ static int is_dir_empty(const wchar_t *wpath)
344
350
int mingw_rmdir (const char * pathname )
345
351
{
346
352
int ret , tries = 0 ;
347
- wchar_t wpathname [MAX_PATH ];
348
- if (xutftowcs_path (wpathname , pathname ) < 0 )
353
+ wchar_t wpathname [MAX_LONG_PATH ];
354
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
349
355
return -1 ;
350
356
351
357
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -422,15 +428,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
422
428
int mingw_mkdir (const char * path , int mode )
423
429
{
424
430
int ret ;
425
- wchar_t wpath [MAX_PATH ];
431
+ wchar_t wpath [MAX_LONG_PATH ];
426
432
427
433
if (!is_valid_win32_path (path , 0 )) {
428
434
errno = EINVAL ;
429
435
return -1 ;
430
436
}
431
437
432
- if (xutftowcs_path (wpath , path ) < 0 )
438
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
439
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
440
+ core_long_paths ) < 0 )
433
441
return -1 ;
442
+
434
443
ret = _wmkdir (wpath );
435
444
if (!ret && needs_hiding (path ))
436
445
return set_hidden_flag (wpath , 1 );
@@ -514,7 +523,7 @@ int mingw_open (const char *filename, int oflags, ...)
514
523
va_list args ;
515
524
unsigned mode ;
516
525
int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
517
- wchar_t wfilename [MAX_PATH ];
526
+ wchar_t wfilename [MAX_LONG_PATH ];
518
527
open_fn_t open_fn ;
519
528
520
529
va_start (args , oflags );
@@ -533,7 +542,7 @@ int mingw_open (const char *filename, int oflags, ...)
533
542
534
543
if (filename && !strcmp (filename , "/dev/null" ))
535
544
wcscpy (wfilename , L"nul" );
536
- else if (xutftowcs_path (wfilename , filename ) < 0 )
545
+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
537
546
return -1 ;
538
547
539
548
fd = open_fn (wfilename , oflags , mode );
@@ -591,14 +600,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
591
600
{
592
601
int hide = needs_hiding (filename );
593
602
FILE * file ;
594
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
603
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
595
604
if (filename && !strcmp (filename , "/dev/null" ))
596
605
wcscpy (wfilename , L"nul" );
597
606
else if (!is_valid_win32_path (filename , 1 )) {
598
607
int create = otype && strchr (otype , 'w' );
599
608
errno = create ? EINVAL : ENOENT ;
600
609
return NULL ;
601
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
610
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
602
611
return NULL ;
603
612
604
613
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -620,14 +629,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
620
629
{
621
630
int hide = needs_hiding (filename );
622
631
FILE * file ;
623
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
632
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
624
633
if (filename && !strcmp (filename , "/dev/null" ))
625
634
wcscpy (wfilename , L"nul" );
626
635
else if (!is_valid_win32_path (filename , 1 )) {
627
636
int create = otype && strchr (otype , 'w' );
628
637
errno = create ? EINVAL : ENOENT ;
629
638
return NULL ;
630
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
639
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
631
640
return NULL ;
632
641
633
642
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -684,25 +693,31 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
684
693
685
694
int mingw_access (const char * filename , int mode )
686
695
{
687
- wchar_t wfilename [MAX_PATH ];
688
- if (xutftowcs_path (wfilename , filename ) < 0 )
696
+ wchar_t wfilename [MAX_LONG_PATH ];
697
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
689
698
return -1 ;
690
699
/* X_OK is not supported by the MSVCRT version */
691
700
return _waccess (wfilename , mode & ~X_OK );
692
701
}
693
702
703
+ /* cached length of current directory for handle_long_path */
704
+ static int current_directory_len = 0 ;
705
+
694
706
int mingw_chdir (const char * dirname )
695
707
{
696
- wchar_t wdirname [MAX_PATH ];
697
- if (xutftowcs_path (wdirname , dirname ) < 0 )
708
+ int result ;
709
+ wchar_t wdirname [MAX_LONG_PATH ];
710
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
698
711
return -1 ;
699
- return _wchdir (wdirname );
712
+ result = _wchdir (wdirname );
713
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
714
+ return result ;
700
715
}
701
716
702
717
int mingw_chmod (const char * filename , int mode )
703
718
{
704
- wchar_t wfilename [MAX_PATH ];
705
- if (xutftowcs_path (wfilename , filename ) < 0 )
719
+ wchar_t wfilename [MAX_LONG_PATH ];
720
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
706
721
return -1 ;
707
722
return _wchmod (wfilename , mode );
708
723
}
@@ -750,8 +765,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
750
765
static int do_lstat (int follow , const char * file_name , struct stat * buf )
751
766
{
752
767
WIN32_FILE_ATTRIBUTE_DATA fdata ;
753
- wchar_t wfilename [MAX_PATH ];
754
- if (xutftowcs_path (wfilename , file_name ) < 0 )
768
+ wchar_t wfilename [MAX_LONG_PATH ];
769
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
755
770
return -1 ;
756
771
757
772
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -922,8 +937,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
922
937
FILETIME mft , aft ;
923
938
int fh , rc ;
924
939
DWORD attrs ;
925
- wchar_t wfilename [MAX_PATH ];
926
- if (xutftowcs_path (wfilename , file_name ) < 0 )
940
+ wchar_t wfilename [MAX_LONG_PATH ];
941
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
927
942
return -1 ;
928
943
929
944
/* must have write permission */
@@ -984,6 +999,7 @@ char *mingw_mktemp(char *template)
984
999
wchar_t wtemplate [MAX_PATH ];
985
1000
int offset = 0 ;
986
1001
1002
+ /* we need to return the path, thus no long paths here! */
987
1003
if (xutftowcs_path (wtemplate , template ) < 0 )
988
1004
return NULL ;
989
1005
@@ -1547,6 +1563,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1547
1563
1548
1564
if (* argv && !strcmp (cmd , * argv ))
1549
1565
wcmd [0 ] = L'\0' ;
1566
+ /*
1567
+ * Paths to executables and to the current directory do not support
1568
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1569
+ */
1550
1570
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1551
1571
return -1 ;
1552
1572
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2198,8 +2218,9 @@ int mingw_rename(const char *pold, const char *pnew)
2198
2218
{
2199
2219
DWORD attrs , gle ;
2200
2220
int tries = 0 ;
2201
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2202
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2221
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2222
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2223
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
2203
2224
return -1 ;
2204
2225
2205
2226
/*
@@ -2513,9 +2534,9 @@ int mingw_raise(int sig)
2513
2534
2514
2535
int link (const char * oldpath , const char * newpath )
2515
2536
{
2516
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2517
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2518
- xutftowcs_path (wnewpath , newpath ) < 0 )
2537
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2538
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2539
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2519
2540
return -1 ;
2520
2541
2521
2542
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2583,8 +2604,8 @@ int mingw_is_mount_point(struct strbuf *path)
2583
2604
{
2584
2605
WIN32_FIND_DATAW findbuf = { 0 };
2585
2606
HANDLE handle ;
2586
- wchar_t wfilename [MAX_PATH ];
2587
- int wlen = xutftowcs_path (wfilename , path -> buf );
2607
+ wchar_t wfilename [MAX_LONG_PATH ];
2608
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
2588
2609
if (wlen < 0 )
2589
2610
die (_ ("could not get long path for '%s'" ), path -> buf );
2590
2611
@@ -2861,6 +2882,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
2861
2882
}
2862
2883
}
2863
2884
2885
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
2886
+ {
2887
+ int result ;
2888
+ wchar_t buf [MAX_LONG_PATH ];
2889
+
2890
+ /*
2891
+ * we don't need special handling if path is relative to the current
2892
+ * directory, and current directory + path don't exceed the desired
2893
+ * max_path limit. This should cover > 99 % of cases with minimal
2894
+ * performance impact (git almost always uses relative paths).
2895
+ */
2896
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
2897
+ (current_directory_len + len < max_path ))
2898
+ return len ;
2899
+
2900
+ /*
2901
+ * handle everything else:
2902
+ * - absolute paths: "C:\dir\file"
2903
+ * - absolute UNC paths: "\\server\share\dir\file"
2904
+ * - absolute paths on current drive: "\dir\file"
2905
+ * - relative paths on other drive: "X:file"
2906
+ * - prefixed paths: "\\?\...", "\\.\..."
2907
+ */
2908
+
2909
+ /* convert to absolute path using GetFullPathNameW */
2910
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
2911
+ if (!result ) {
2912
+ errno = err_win_to_posix (GetLastError ());
2913
+ return -1 ;
2914
+ }
2915
+
2916
+ /*
2917
+ * return absolute path if it fits within max_path (even if
2918
+ * "cwd + path" doesn't due to '..' components)
2919
+ */
2920
+ if (result < max_path ) {
2921
+ wcscpy (path , buf );
2922
+ return result ;
2923
+ }
2924
+
2925
+ /* error out if we shouldn't expand the path or buf is too small */
2926
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
2927
+ errno = ENAMETOOLONG ;
2928
+ return -1 ;
2929
+ }
2930
+
2931
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
2932
+ if (buf [0 ] == '\\' ) {
2933
+ /* ...unless already prefixed */
2934
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
2935
+ return len ;
2936
+
2937
+ wcscpy (path , L"\\\\?\\UNC\\" );
2938
+ wcscpy (path + 8 , buf + 2 );
2939
+ return result + 6 ;
2940
+ } else {
2941
+ wcscpy (path , L"\\\\?\\" );
2942
+ wcscpy (path + 4 , buf );
2943
+ return result + 4 ;
2944
+ }
2945
+ }
2946
+
2864
2947
#if !defined(_MSC_VER )
2865
2948
/*
2866
2949
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3022,6 +3105,9 @@ int wmain(int argc, const wchar_t **wargv)
3022
3105
/* initialize Unicode console */
3023
3106
winansi_init ();
3024
3107
3108
+ /* init length of current directory for handle_long_path */
3109
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3110
+
3025
3111
/* invoke the real main() using our utf8 version of argv. */
3026
3112
exit_status = main (argc , argv );
3027
3113
0 commit comments