@@ -235,6 +235,7 @@ static int core_restrict_inherited_handles = -1;
235
235
static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY ;
236
236
static char * unset_environment_variables ;
237
237
int core_fscache ;
238
+ int core_long_paths ;
238
239
239
240
int mingw_core_config (const char * var , const char * value , void * cb )
240
241
{
@@ -251,6 +252,11 @@ int mingw_core_config(const char *var, const char *value, void *cb)
251
252
return 0 ;
252
253
}
253
254
255
+ if (!strcmp (var , "core.longpaths" )) {
256
+ core_long_paths = git_config_bool (var , value );
257
+ return 0 ;
258
+ }
259
+
254
260
if (!strcmp (var , "core.unsetenvvars" )) {
255
261
free (unset_environment_variables );
256
262
unset_environment_variables = xstrdup (value );
@@ -297,8 +303,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
297
303
int mingw_unlink (const char * pathname )
298
304
{
299
305
int ret , tries = 0 ;
300
- wchar_t wpathname [MAX_PATH ];
301
- if (xutftowcs_path (wpathname , pathname ) < 0 )
306
+ wchar_t wpathname [MAX_LONG_PATH ];
307
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
302
308
return -1 ;
303
309
304
310
if (DeleteFileW (wpathname ))
@@ -330,7 +336,7 @@ static int is_dir_empty(const wchar_t *wpath)
330
336
{
331
337
WIN32_FIND_DATAW findbuf ;
332
338
HANDLE handle ;
333
- wchar_t wbuf [MAX_PATH + 2 ];
339
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
334
340
wcscpy (wbuf , wpath );
335
341
wcscat (wbuf , L"\\*" );
336
342
handle = FindFirstFileW (wbuf , & findbuf );
@@ -351,7 +357,7 @@ static int is_dir_empty(const wchar_t *wpath)
351
357
int mingw_rmdir (const char * pathname )
352
358
{
353
359
int ret , tries = 0 ;
354
- wchar_t wpathname [MAX_PATH ];
360
+ wchar_t wpathname [MAX_LONG_PATH ];
355
361
struct stat st ;
356
362
357
363
/*
@@ -373,7 +379,7 @@ int mingw_rmdir(const char *pathname)
373
379
return -1 ;
374
380
}
375
381
376
- if (xutftowcs_path (wpathname , pathname ) < 0 )
382
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
377
383
return -1 ;
378
384
379
385
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -452,15 +458,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
452
458
int mingw_mkdir (const char * path , int mode )
453
459
{
454
460
int ret ;
455
- wchar_t wpath [MAX_PATH ];
461
+ wchar_t wpath [MAX_LONG_PATH ];
456
462
457
463
if (!is_valid_win32_path (path , 0 )) {
458
464
errno = EINVAL ;
459
465
return -1 ;
460
466
}
461
467
462
- if (xutftowcs_path (wpath , path ) < 0 )
468
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
469
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
470
+ core_long_paths ) < 0 )
463
471
return -1 ;
472
+
464
473
ret = _wmkdir (wpath );
465
474
if (!ret && needs_hiding (path ))
466
475
return set_hidden_flag (wpath , 1 );
@@ -547,7 +556,7 @@ int mingw_open (const char *filename, int oflags, ...)
547
556
va_list args ;
548
557
unsigned mode ;
549
558
int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
550
- wchar_t wfilename [MAX_PATH ];
559
+ wchar_t wfilename [MAX_LONG_PATH ];
551
560
open_fn_t open_fn ;
552
561
553
562
va_start (args , oflags );
@@ -575,7 +584,7 @@ int mingw_open (const char *filename, int oflags, ...)
575
584
576
585
if (filename && !strcmp (filename , "/dev/null" ))
577
586
wcscpy (wfilename , L"nul" );
578
- else if (xutftowcs_path (wfilename , filename ) < 0 )
587
+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
579
588
return -1 ;
580
589
581
590
fd = open_fn (wfilename , oflags , mode );
@@ -633,14 +642,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
633
642
{
634
643
int hide = needs_hiding (filename );
635
644
FILE * file ;
636
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
645
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
637
646
if (filename && !strcmp (filename , "/dev/null" ))
638
647
wcscpy (wfilename , L"nul" );
639
648
else if (!is_valid_win32_path (filename , 1 )) {
640
649
int create = otype && strchr (otype , 'w' );
641
650
errno = create ? EINVAL : ENOENT ;
642
651
return NULL ;
643
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
652
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
644
653
return NULL ;
645
654
646
655
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -662,14 +671,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
662
671
{
663
672
int hide = needs_hiding (filename );
664
673
FILE * file ;
665
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
674
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
666
675
if (filename && !strcmp (filename , "/dev/null" ))
667
676
wcscpy (wfilename , L"nul" );
668
677
else if (!is_valid_win32_path (filename , 1 )) {
669
678
int create = otype && strchr (otype , 'w' );
670
679
errno = create ? EINVAL : ENOENT ;
671
680
return NULL ;
672
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
681
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
673
682
return NULL ;
674
683
675
684
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -744,27 +753,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
744
753
745
754
int mingw_access (const char * filename , int mode )
746
755
{
747
- wchar_t wfilename [MAX_PATH ];
756
+ wchar_t wfilename [MAX_LONG_PATH ];
748
757
if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
749
758
return 0 ;
750
- if (xutftowcs_path (wfilename , filename ) < 0 )
759
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
751
760
return -1 ;
752
761
/* X_OK is not supported by the MSVCRT version */
753
762
return _waccess (wfilename , mode & ~X_OK );
754
763
}
755
764
765
+ /* cached length of current directory for handle_long_path */
766
+ static int current_directory_len = 0 ;
767
+
756
768
int mingw_chdir (const char * dirname )
757
769
{
758
- wchar_t wdirname [MAX_PATH ];
759
- if (xutftowcs_path (wdirname , dirname ) < 0 )
770
+ int result ;
771
+ wchar_t wdirname [MAX_LONG_PATH ];
772
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
760
773
return -1 ;
761
- return _wchdir (wdirname );
774
+ result = _wchdir (wdirname );
775
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
776
+ return result ;
762
777
}
763
778
764
779
int mingw_chmod (const char * filename , int mode )
765
780
{
766
- wchar_t wfilename [MAX_PATH ];
767
- if (xutftowcs_path (wfilename , filename ) < 0 )
781
+ wchar_t wfilename [MAX_LONG_PATH ];
782
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
768
783
return -1 ;
769
784
return _wchmod (wfilename , mode );
770
785
}
@@ -812,8 +827,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
812
827
static int do_lstat (int follow , const char * file_name , struct stat * buf )
813
828
{
814
829
WIN32_FILE_ATTRIBUTE_DATA fdata ;
815
- wchar_t wfilename [MAX_PATH ];
816
- if (xutftowcs_path (wfilename , file_name ) < 0 )
830
+ wchar_t wfilename [MAX_LONG_PATH ];
831
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
817
832
return -1 ;
818
833
819
834
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -984,10 +999,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
984
999
FILETIME mft , aft ;
985
1000
int rc ;
986
1001
DWORD attrs ;
987
- wchar_t wfilename [MAX_PATH ];
1002
+ wchar_t wfilename [MAX_LONG_PATH ];
988
1003
HANDLE osfilehandle ;
989
1004
990
- if (xutftowcs_path (wfilename , file_name ) < 0 )
1005
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
991
1006
return -1 ;
992
1007
993
1008
/* must have write permission */
@@ -1070,6 +1085,7 @@ char *mingw_mktemp(char *template)
1070
1085
wchar_t wtemplate [MAX_PATH ];
1071
1086
int offset = 0 ;
1072
1087
1088
+ /* we need to return the path, thus no long paths here! */
1073
1089
if (xutftowcs_path (wtemplate , template ) < 0 )
1074
1090
return NULL ;
1075
1091
@@ -1707,6 +1723,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1707
1723
1708
1724
if (* argv && !strcmp (cmd , * argv ))
1709
1725
wcmd [0 ] = L'\0' ;
1726
+ /*
1727
+ * Paths to executables and to the current directory do not support
1728
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1729
+ */
1710
1730
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1711
1731
return -1 ;
1712
1732
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2358,8 +2378,9 @@ int mingw_rename(const char *pold, const char *pnew)
2358
2378
{
2359
2379
DWORD attrs , gle ;
2360
2380
int tries = 0 ;
2361
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2362
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2381
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2382
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2383
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
2363
2384
return -1 ;
2364
2385
2365
2386
/*
@@ -2673,9 +2694,9 @@ int mingw_raise(int sig)
2673
2694
2674
2695
int link (const char * oldpath , const char * newpath )
2675
2696
{
2676
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2677
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2678
- xutftowcs_path (wnewpath , newpath ) < 0 )
2697
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2698
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2699
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2679
2700
return -1 ;
2680
2701
2681
2702
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2743,8 +2764,8 @@ int mingw_is_mount_point(struct strbuf *path)
2743
2764
{
2744
2765
WIN32_FIND_DATAW findbuf = { 0 };
2745
2766
HANDLE handle ;
2746
- wchar_t wfilename [MAX_PATH ];
2747
- int wlen = xutftowcs_path (wfilename , path -> buf );
2767
+ wchar_t wfilename [MAX_LONG_PATH ];
2768
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
2748
2769
if (wlen < 0 )
2749
2770
die (_ ("could not get long path for '%s'" ), path -> buf );
2750
2771
@@ -2889,9 +2910,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
2889
2910
2890
2911
static int is_system32_path (const char * path )
2891
2912
{
2892
- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
2913
+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
2893
2914
2894
- if (xutftowcs_path (wpath , path ) < 0 ||
2915
+ if (xutftowcs_long_path (wpath , path ) < 0 ||
2895
2916
!GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
2896
2917
_wcsicmp (system32 , wpath ))
2897
2918
return 0 ;
@@ -3256,6 +3277,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
3256
3277
}
3257
3278
}
3258
3279
3280
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3281
+ {
3282
+ int result ;
3283
+ wchar_t buf [MAX_LONG_PATH ];
3284
+
3285
+ /*
3286
+ * we don't need special handling if path is relative to the current
3287
+ * directory, and current directory + path don't exceed the desired
3288
+ * max_path limit. This should cover > 99 % of cases with minimal
3289
+ * performance impact (git almost always uses relative paths).
3290
+ */
3291
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3292
+ (current_directory_len + len < max_path ))
3293
+ return len ;
3294
+
3295
+ /*
3296
+ * handle everything else:
3297
+ * - absolute paths: "C:\dir\file"
3298
+ * - absolute UNC paths: "\\server\share\dir\file"
3299
+ * - absolute paths on current drive: "\dir\file"
3300
+ * - relative paths on other drive: "X:file"
3301
+ * - prefixed paths: "\\?\...", "\\.\..."
3302
+ */
3303
+
3304
+ /* convert to absolute path using GetFullPathNameW */
3305
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3306
+ if (!result ) {
3307
+ errno = err_win_to_posix (GetLastError ());
3308
+ return -1 ;
3309
+ }
3310
+
3311
+ /*
3312
+ * return absolute path if it fits within max_path (even if
3313
+ * "cwd + path" doesn't due to '..' components)
3314
+ */
3315
+ if (result < max_path ) {
3316
+ wcscpy (path , buf );
3317
+ return result ;
3318
+ }
3319
+
3320
+ /* error out if we shouldn't expand the path or buf is too small */
3321
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3322
+ errno = ENAMETOOLONG ;
3323
+ return -1 ;
3324
+ }
3325
+
3326
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3327
+ if (buf [0 ] == '\\' ) {
3328
+ /* ...unless already prefixed */
3329
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3330
+ return len ;
3331
+
3332
+ wcscpy (path , L"\\\\?\\UNC\\" );
3333
+ wcscpy (path + 8 , buf + 2 );
3334
+ return result + 6 ;
3335
+ } else {
3336
+ wcscpy (path , L"\\\\?\\" );
3337
+ wcscpy (path + 4 , buf );
3338
+ return result + 4 ;
3339
+ }
3340
+ }
3341
+
3259
3342
#if !defined(_MSC_VER )
3260
3343
/*
3261
3344
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3417,6 +3500,9 @@ int wmain(int argc, const wchar_t **wargv)
3417
3500
/* initialize Unicode console */
3418
3501
winansi_init ();
3419
3502
3503
+ /* init length of current directory for handle_long_path */
3504
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3505
+
3420
3506
/* invoke the real main() using our utf8 version of argv. */
3421
3507
exit_status = main (argc , argv );
3422
3508
0 commit comments