@@ -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
@@ -1629,6 +1645,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1629
1645
1630
1646
if (* argv && !strcmp (cmd , * argv ))
1631
1647
wcmd [0 ] = L'\0' ;
1648
+ /*
1649
+ * Paths to executables and to the current directory do not support
1650
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1651
+ */
1632
1652
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1633
1653
return -1 ;
1634
1654
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2280,8 +2300,9 @@ int mingw_rename(const char *pold, const char *pnew)
2280
2300
{
2281
2301
DWORD attrs , gle ;
2282
2302
int tries = 0 ;
2283
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2284
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2303
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2304
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2305
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
2285
2306
return -1 ;
2286
2307
2287
2308
/*
@@ -2595,9 +2616,9 @@ int mingw_raise(int sig)
2595
2616
2596
2617
int link (const char * oldpath , const char * newpath )
2597
2618
{
2598
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2599
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2600
- xutftowcs_path (wnewpath , newpath ) < 0 )
2619
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2620
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2621
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2601
2622
return -1 ;
2602
2623
2603
2624
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2665,8 +2686,8 @@ int mingw_is_mount_point(struct strbuf *path)
2665
2686
{
2666
2687
WIN32_FIND_DATAW findbuf = { 0 };
2667
2688
HANDLE handle ;
2668
- wchar_t wfilename [MAX_PATH ];
2669
- int wlen = xutftowcs_path (wfilename , path -> buf );
2689
+ wchar_t wfilename [MAX_LONG_PATH ];
2690
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
2670
2691
if (wlen < 0 )
2671
2692
die (_ ("could not get long path for '%s'" ), path -> buf );
2672
2693
@@ -3030,6 +3051,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
3030
3051
}
3031
3052
}
3032
3053
3054
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3055
+ {
3056
+ int result ;
3057
+ wchar_t buf [MAX_LONG_PATH ];
3058
+
3059
+ /*
3060
+ * we don't need special handling if path is relative to the current
3061
+ * directory, and current directory + path don't exceed the desired
3062
+ * max_path limit. This should cover > 99 % of cases with minimal
3063
+ * performance impact (git almost always uses relative paths).
3064
+ */
3065
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3066
+ (current_directory_len + len < max_path ))
3067
+ return len ;
3068
+
3069
+ /*
3070
+ * handle everything else:
3071
+ * - absolute paths: "C:\dir\file"
3072
+ * - absolute UNC paths: "\\server\share\dir\file"
3073
+ * - absolute paths on current drive: "\dir\file"
3074
+ * - relative paths on other drive: "X:file"
3075
+ * - prefixed paths: "\\?\...", "\\.\..."
3076
+ */
3077
+
3078
+ /* convert to absolute path using GetFullPathNameW */
3079
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3080
+ if (!result ) {
3081
+ errno = err_win_to_posix (GetLastError ());
3082
+ return -1 ;
3083
+ }
3084
+
3085
+ /*
3086
+ * return absolute path if it fits within max_path (even if
3087
+ * "cwd + path" doesn't due to '..' components)
3088
+ */
3089
+ if (result < max_path ) {
3090
+ wcscpy (path , buf );
3091
+ return result ;
3092
+ }
3093
+
3094
+ /* error out if we shouldn't expand the path or buf is too small */
3095
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3096
+ errno = ENAMETOOLONG ;
3097
+ return -1 ;
3098
+ }
3099
+
3100
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3101
+ if (buf [0 ] == '\\' ) {
3102
+ /* ...unless already prefixed */
3103
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3104
+ return len ;
3105
+
3106
+ wcscpy (path , L"\\\\?\\UNC\\" );
3107
+ wcscpy (path + 8 , buf + 2 );
3108
+ return result + 6 ;
3109
+ } else {
3110
+ wcscpy (path , L"\\\\?\\" );
3111
+ wcscpy (path + 4 , buf );
3112
+ return result + 4 ;
3113
+ }
3114
+ }
3115
+
3033
3116
#if !defined(_MSC_VER )
3034
3117
/*
3035
3118
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3191,6 +3274,9 @@ int wmain(int argc, const wchar_t **wargv)
3191
3274
/* initialize Unicode console */
3192
3275
winansi_init ();
3193
3276
3277
+ /* init length of current directory for handle_long_path */
3278
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3279
+
3194
3280
/* invoke the real main() using our utf8 version of argv. */
3195
3281
exit_status = main (argc , argv );
3196
3282
0 commit comments