@@ -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,8 +353,8 @@ 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 ];
351
- if (xutftowcs_path (wpathname , pathname ) < 0 )
356
+ wchar_t wpathname [MAX_LONG_PATH ];
357
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
352
358
return -1 ;
353
359
354
360
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -425,15 +431,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
425
431
int mingw_mkdir (const char * path , int mode )
426
432
{
427
433
int ret ;
428
- wchar_t wpath [MAX_PATH ];
434
+ wchar_t wpath [MAX_LONG_PATH ];
429
435
430
436
if (!is_valid_win32_path (path , 0 )) {
431
437
errno = EINVAL ;
432
438
return -1 ;
433
439
}
434
440
435
- if (xutftowcs_path (wpath , path ) < 0 )
441
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
442
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
443
+ core_long_paths ) < 0 )
436
444
return -1 ;
445
+
437
446
ret = _wmkdir (wpath );
438
447
if (!ret && needs_hiding (path ))
439
448
return set_hidden_flag (wpath , 1 );
@@ -519,7 +528,7 @@ int mingw_open (const char *filename, int oflags, ...)
519
528
va_list args ;
520
529
unsigned mode ;
521
530
int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
522
- wchar_t wfilename [MAX_PATH ];
531
+ wchar_t wfilename [MAX_LONG_PATH ];
523
532
open_fn_t open_fn ;
524
533
525
534
va_start (args , oflags );
@@ -538,7 +547,7 @@ int mingw_open (const char *filename, int oflags, ...)
538
547
539
548
if (filename && !strcmp (filename , "/dev/null" ))
540
549
wcscpy (wfilename , L"nul" );
541
- else if (xutftowcs_path (wfilename , filename ) < 0 )
550
+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
542
551
return -1 ;
543
552
544
553
fd = open_fn (wfilename , oflags , mode );
@@ -596,14 +605,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
596
605
{
597
606
int hide = needs_hiding (filename );
598
607
FILE * file ;
599
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
608
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
600
609
if (filename && !strcmp (filename , "/dev/null" ))
601
610
wcscpy (wfilename , L"nul" );
602
611
else if (!is_valid_win32_path (filename , 1 )) {
603
612
int create = otype && strchr (otype , 'w' );
604
613
errno = create ? EINVAL : ENOENT ;
605
614
return NULL ;
606
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
615
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
607
616
return NULL ;
608
617
609
618
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -625,14 +634,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
625
634
{
626
635
int hide = needs_hiding (filename );
627
636
FILE * file ;
628
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
637
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
629
638
if (filename && !strcmp (filename , "/dev/null" ))
630
639
wcscpy (wfilename , L"nul" );
631
640
else if (!is_valid_win32_path (filename , 1 )) {
632
641
int create = otype && strchr (otype , 'w' );
633
642
errno = create ? EINVAL : ENOENT ;
634
643
return NULL ;
635
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
644
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
636
645
return NULL ;
637
646
638
647
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -689,25 +698,31 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
689
698
690
699
int mingw_access (const char * filename , int mode )
691
700
{
692
- wchar_t wfilename [MAX_PATH ];
693
- if (xutftowcs_path (wfilename , filename ) < 0 )
701
+ wchar_t wfilename [MAX_LONG_PATH ];
702
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
694
703
return -1 ;
695
704
/* X_OK is not supported by the MSVCRT version */
696
705
return _waccess (wfilename , mode & ~X_OK );
697
706
}
698
707
708
+ /* cached length of current directory for handle_long_path */
709
+ static int current_directory_len = 0 ;
710
+
699
711
int mingw_chdir (const char * dirname )
700
712
{
701
- wchar_t wdirname [MAX_PATH ];
702
- if (xutftowcs_path (wdirname , dirname ) < 0 )
713
+ int result ;
714
+ wchar_t wdirname [MAX_LONG_PATH ];
715
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
703
716
return -1 ;
704
- return _wchdir (wdirname );
717
+ result = _wchdir (wdirname );
718
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
719
+ return result ;
705
720
}
706
721
707
722
int mingw_chmod (const char * filename , int mode )
708
723
{
709
- wchar_t wfilename [MAX_PATH ];
710
- if (xutftowcs_path (wfilename , filename ) < 0 )
724
+ wchar_t wfilename [MAX_LONG_PATH ];
725
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
711
726
return -1 ;
712
727
return _wchmod (wfilename , mode );
713
728
}
@@ -755,8 +770,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
755
770
static int do_lstat (int follow , const char * file_name , struct stat * buf )
756
771
{
757
772
WIN32_FILE_ATTRIBUTE_DATA fdata ;
758
- wchar_t wfilename [MAX_PATH ];
759
- if (xutftowcs_path (wfilename , file_name ) < 0 )
773
+ wchar_t wfilename [MAX_LONG_PATH ];
774
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
760
775
return -1 ;
761
776
762
777
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -927,8 +942,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
927
942
FILETIME mft , aft ;
928
943
int fh , rc ;
929
944
DWORD attrs ;
930
- wchar_t wfilename [MAX_PATH ];
931
- if (xutftowcs_path (wfilename , file_name ) < 0 )
945
+ wchar_t wfilename [MAX_LONG_PATH ];
946
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
932
947
return -1 ;
933
948
934
949
/* must have write permission */
@@ -998,6 +1013,7 @@ char *mingw_mktemp(char *template)
998
1013
wchar_t wtemplate [MAX_PATH ];
999
1014
int offset = 0 ;
1000
1015
1016
+ /* we need to return the path, thus no long paths here! */
1001
1017
if (xutftowcs_path (wtemplate , template ) < 0 )
1002
1018
return NULL ;
1003
1019
@@ -1597,6 +1613,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1597
1613
1598
1614
if (* argv && !strcmp (cmd , * argv ))
1599
1615
wcmd [0 ] = L'\0' ;
1616
+ /*
1617
+ * Paths to executables and to the current directory do not support
1618
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1619
+ */
1600
1620
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1601
1621
return -1 ;
1602
1622
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2248,8 +2268,9 @@ int mingw_rename(const char *pold, const char *pnew)
2248
2268
{
2249
2269
DWORD attrs , gle ;
2250
2270
int tries = 0 ;
2251
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2252
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2271
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2272
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2273
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
2253
2274
return -1 ;
2254
2275
2255
2276
/*
@@ -2563,9 +2584,9 @@ int mingw_raise(int sig)
2563
2584
2564
2585
int link (const char * oldpath , const char * newpath )
2565
2586
{
2566
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2567
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2568
- xutftowcs_path (wnewpath , newpath ) < 0 )
2587
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2588
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2589
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2569
2590
return -1 ;
2570
2591
2571
2592
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2633,8 +2654,8 @@ int mingw_is_mount_point(struct strbuf *path)
2633
2654
{
2634
2655
WIN32_FIND_DATAW findbuf = { 0 };
2635
2656
HANDLE handle ;
2636
- wchar_t wfilename [MAX_PATH ];
2637
- int wlen = xutftowcs_path (wfilename , path -> buf );
2657
+ wchar_t wfilename [MAX_LONG_PATH ];
2658
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
2638
2659
if (wlen < 0 )
2639
2660
die (_ ("could not get long path for '%s'" ), path -> buf );
2640
2661
@@ -2985,6 +3006,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
2985
3006
}
2986
3007
}
2987
3008
3009
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3010
+ {
3011
+ int result ;
3012
+ wchar_t buf [MAX_LONG_PATH ];
3013
+
3014
+ /*
3015
+ * we don't need special handling if path is relative to the current
3016
+ * directory, and current directory + path don't exceed the desired
3017
+ * max_path limit. This should cover > 99 % of cases with minimal
3018
+ * performance impact (git almost always uses relative paths).
3019
+ */
3020
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3021
+ (current_directory_len + len < max_path ))
3022
+ return len ;
3023
+
3024
+ /*
3025
+ * handle everything else:
3026
+ * - absolute paths: "C:\dir\file"
3027
+ * - absolute UNC paths: "\\server\share\dir\file"
3028
+ * - absolute paths on current drive: "\dir\file"
3029
+ * - relative paths on other drive: "X:file"
3030
+ * - prefixed paths: "\\?\...", "\\.\..."
3031
+ */
3032
+
3033
+ /* convert to absolute path using GetFullPathNameW */
3034
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3035
+ if (!result ) {
3036
+ errno = err_win_to_posix (GetLastError ());
3037
+ return -1 ;
3038
+ }
3039
+
3040
+ /*
3041
+ * return absolute path if it fits within max_path (even if
3042
+ * "cwd + path" doesn't due to '..' components)
3043
+ */
3044
+ if (result < max_path ) {
3045
+ wcscpy (path , buf );
3046
+ return result ;
3047
+ }
3048
+
3049
+ /* error out if we shouldn't expand the path or buf is too small */
3050
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3051
+ errno = ENAMETOOLONG ;
3052
+ return -1 ;
3053
+ }
3054
+
3055
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3056
+ if (buf [0 ] == '\\' ) {
3057
+ /* ...unless already prefixed */
3058
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3059
+ return len ;
3060
+
3061
+ wcscpy (path , L"\\\\?\\UNC\\" );
3062
+ wcscpy (path + 8 , buf + 2 );
3063
+ return result + 6 ;
3064
+ } else {
3065
+ wcscpy (path , L"\\\\?\\" );
3066
+ wcscpy (path + 4 , buf );
3067
+ return result + 4 ;
3068
+ }
3069
+ }
3070
+
2988
3071
#if !defined(_MSC_VER )
2989
3072
/*
2990
3073
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3146,6 +3229,9 @@ int wmain(int argc, const wchar_t **wargv)
3146
3229
/* initialize Unicode console */
3147
3230
winansi_init ();
3148
3231
3232
+ /* init length of current directory for handle_long_path */
3233
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3234
+
3149
3235
/* invoke the real main() using our utf8 version of argv. */
3150
3236
exit_status = main (argc , argv );
3151
3237
0 commit comments