@@ -239,6 +239,27 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
239
239
static char * unset_environment_variables ;
240
240
int core_fscache ;
241
241
242
+ int are_long_paths_enabled (void )
243
+ {
244
+ /* default to `false` during initialization */
245
+ static const int fallback = 0 ;
246
+
247
+ static int enabled = -1 ;
248
+
249
+ if (enabled < 0 ) {
250
+ /* avoid infinite recursion */
251
+ if (!the_repository )
252
+ return fallback ;
253
+
254
+ if (the_repository -> config &&
255
+ the_repository -> config -> hash_initialized &&
256
+ git_config_get_bool ("core.longpaths" , & enabled ) < 0 )
257
+ enabled = 0 ;
258
+ }
259
+
260
+ return enabled < 0 ? fallback : enabled ;
261
+ }
262
+
242
263
int mingw_core_config (const char * var , const char * value , void * cb )
243
264
{
244
265
if (!strcmp (var , "core.hidedotfiles" )) {
@@ -300,8 +321,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
300
321
int mingw_unlink (const char * pathname )
301
322
{
302
323
int ret , tries = 0 ;
303
- wchar_t wpathname [MAX_PATH ];
304
- if (xutftowcs_path (wpathname , pathname ) < 0 )
324
+ wchar_t wpathname [MAX_LONG_PATH ];
325
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
305
326
return -1 ;
306
327
307
328
if (DeleteFileW (wpathname ))
@@ -333,7 +354,7 @@ static int is_dir_empty(const wchar_t *wpath)
333
354
{
334
355
WIN32_FIND_DATAW findbuf ;
335
356
HANDLE handle ;
336
- wchar_t wbuf [MAX_PATH + 2 ];
357
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
337
358
wcscpy (wbuf , wpath );
338
359
wcscat (wbuf , L"\\*" );
339
360
handle = FindFirstFileW (wbuf , & findbuf );
@@ -354,7 +375,7 @@ static int is_dir_empty(const wchar_t *wpath)
354
375
int mingw_rmdir (const char * pathname )
355
376
{
356
377
int ret , tries = 0 ;
357
- wchar_t wpathname [MAX_PATH ];
378
+ wchar_t wpathname [MAX_LONG_PATH ];
358
379
struct stat st ;
359
380
360
381
/*
@@ -376,7 +397,7 @@ int mingw_rmdir(const char *pathname)
376
397
return -1 ;
377
398
}
378
399
379
- if (xutftowcs_path (wpathname , pathname ) < 0 )
400
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
380
401
return -1 ;
381
402
382
403
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -455,15 +476,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
455
476
int mingw_mkdir (const char * path , int mode )
456
477
{
457
478
int ret ;
458
- wchar_t wpath [MAX_PATH ];
479
+ wchar_t wpath [MAX_LONG_PATH ];
459
480
460
481
if (!is_valid_win32_path (path , 0 )) {
461
482
errno = EINVAL ;
462
483
return -1 ;
463
484
}
464
485
465
- if (xutftowcs_path (wpath , path ) < 0 )
486
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
487
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
488
+ are_long_paths_enabled ()) < 0 )
466
489
return -1 ;
490
+
467
491
ret = _wmkdir (wpath );
468
492
if (!ret && needs_hiding (path ))
469
493
return set_hidden_flag (wpath , 1 );
@@ -550,7 +574,7 @@ int mingw_open (const char *filename, int oflags, ...)
550
574
va_list args ;
551
575
unsigned mode ;
552
576
int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
553
- wchar_t wfilename [MAX_PATH ];
577
+ wchar_t wfilename [MAX_LONG_PATH ];
554
578
open_fn_t open_fn ;
555
579
556
580
va_start (args , oflags );
@@ -578,7 +602,7 @@ int mingw_open (const char *filename, int oflags, ...)
578
602
579
603
if (filename && !strcmp (filename , "/dev/null" ))
580
604
wcscpy (wfilename , L"nul" );
581
- else if (xutftowcs_path (wfilename , filename ) < 0 )
605
+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
582
606
return -1 ;
583
607
584
608
fd = open_fn (wfilename , oflags , mode );
@@ -636,14 +660,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
636
660
{
637
661
int hide = needs_hiding (filename );
638
662
FILE * file ;
639
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
663
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
640
664
if (filename && !strcmp (filename , "/dev/null" ))
641
665
wcscpy (wfilename , L"nul" );
642
666
else if (!is_valid_win32_path (filename , 1 )) {
643
667
int create = otype && strchr (otype , 'w' );
644
668
errno = create ? EINVAL : ENOENT ;
645
669
return NULL ;
646
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
670
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
647
671
return NULL ;
648
672
649
673
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -665,14 +689,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
665
689
{
666
690
int hide = needs_hiding (filename );
667
691
FILE * file ;
668
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
692
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
669
693
if (filename && !strcmp (filename , "/dev/null" ))
670
694
wcscpy (wfilename , L"nul" );
671
695
else if (!is_valid_win32_path (filename , 1 )) {
672
696
int create = otype && strchr (otype , 'w' );
673
697
errno = create ? EINVAL : ENOENT ;
674
698
return NULL ;
675
- } else if (xutftowcs_path (wfilename , filename ) < 0 )
699
+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
676
700
return NULL ;
677
701
678
702
if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -747,27 +771,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
747
771
748
772
int mingw_access (const char * filename , int mode )
749
773
{
750
- wchar_t wfilename [MAX_PATH ];
774
+ wchar_t wfilename [MAX_LONG_PATH ];
751
775
if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
752
776
return 0 ;
753
- if (xutftowcs_path (wfilename , filename ) < 0 )
777
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
754
778
return -1 ;
755
779
/* X_OK is not supported by the MSVCRT version */
756
780
return _waccess (wfilename , mode & ~X_OK );
757
781
}
758
782
783
+ /* cached length of current directory for handle_long_path */
784
+ static int current_directory_len = 0 ;
785
+
759
786
int mingw_chdir (const char * dirname )
760
787
{
761
- wchar_t wdirname [MAX_PATH ];
762
- if (xutftowcs_path (wdirname , dirname ) < 0 )
788
+ int result ;
789
+ wchar_t wdirname [MAX_LONG_PATH ];
790
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
763
791
return -1 ;
764
- return _wchdir (wdirname );
792
+ result = _wchdir (wdirname );
793
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
794
+ return result ;
765
795
}
766
796
767
797
int mingw_chmod (const char * filename , int mode )
768
798
{
769
- wchar_t wfilename [MAX_PATH ];
770
- if (xutftowcs_path (wfilename , filename ) < 0 )
799
+ wchar_t wfilename [MAX_LONG_PATH ];
800
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
771
801
return -1 ;
772
802
return _wchmod (wfilename , mode );
773
803
}
@@ -815,8 +845,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
815
845
static int do_lstat (int follow , const char * file_name , struct stat * buf )
816
846
{
817
847
WIN32_FILE_ATTRIBUTE_DATA fdata ;
818
- wchar_t wfilename [MAX_PATH ];
819
- if (xutftowcs_path (wfilename , file_name ) < 0 )
848
+ wchar_t wfilename [MAX_LONG_PATH ];
849
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
820
850
return -1 ;
821
851
822
852
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -987,10 +1017,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
987
1017
FILETIME mft , aft ;
988
1018
int rc ;
989
1019
DWORD attrs ;
990
- wchar_t wfilename [MAX_PATH ];
1020
+ wchar_t wfilename [MAX_LONG_PATH ];
991
1021
HANDLE osfilehandle ;
992
1022
993
- if (xutftowcs_path (wfilename , file_name ) < 0 )
1023
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
994
1024
return -1 ;
995
1025
996
1026
/* must have write permission */
@@ -1073,6 +1103,7 @@ char *mingw_mktemp(char *template)
1073
1103
wchar_t wtemplate [MAX_PATH ];
1074
1104
int offset = 0 ;
1075
1105
1106
+ /* we need to return the path, thus no long paths here! */
1076
1107
if (xutftowcs_path (wtemplate , template ) < 0 )
1077
1108
return NULL ;
1078
1109
@@ -1707,6 +1738,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1707
1738
1708
1739
if (* argv && !strcmp (cmd , * argv ))
1709
1740
wcmd [0 ] = L'\0' ;
1741
+ /*
1742
+ * Paths to executables and to the current directory do not support
1743
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1744
+ */
1710
1745
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1711
1746
return -1 ;
1712
1747
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2358,8 +2393,9 @@ int mingw_rename(const char *pold, const char *pnew)
2358
2393
{
2359
2394
DWORD attrs , gle ;
2360
2395
int tries = 0 ;
2361
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2362
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2396
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2397
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2398
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
2363
2399
return -1 ;
2364
2400
2365
2401
/*
@@ -2673,9 +2709,9 @@ int mingw_raise(int sig)
2673
2709
2674
2710
int link (const char * oldpath , const char * newpath )
2675
2711
{
2676
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2677
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2678
- xutftowcs_path (wnewpath , newpath ) < 0 )
2712
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2713
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2714
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2679
2715
return -1 ;
2680
2716
2681
2717
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2743,8 +2779,8 @@ int mingw_is_mount_point(struct strbuf *path)
2743
2779
{
2744
2780
WIN32_FIND_DATAW findbuf = { 0 };
2745
2781
HANDLE handle ;
2746
- wchar_t wfilename [MAX_PATH ];
2747
- int wlen = xutftowcs_path (wfilename , path -> buf );
2782
+ wchar_t wfilename [MAX_LONG_PATH ];
2783
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
2748
2784
if (wlen < 0 )
2749
2785
die (_ ("could not get long path for '%s'" ), path -> buf );
2750
2786
@@ -2889,9 +2925,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
2889
2925
2890
2926
static int is_system32_path (const char * path )
2891
2927
{
2892
- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
2928
+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
2893
2929
2894
- if (xutftowcs_path (wpath , path ) < 0 ||
2930
+ if (xutftowcs_long_path (wpath , path ) < 0 ||
2895
2931
!GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
2896
2932
_wcsicmp (system32 , wpath ))
2897
2933
return 0 ;
@@ -3259,6 +3295,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
3259
3295
}
3260
3296
}
3261
3297
3298
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3299
+ {
3300
+ int result ;
3301
+ wchar_t buf [MAX_LONG_PATH ];
3302
+
3303
+ /*
3304
+ * we don't need special handling if path is relative to the current
3305
+ * directory, and current directory + path don't exceed the desired
3306
+ * max_path limit. This should cover > 99 % of cases with minimal
3307
+ * performance impact (git almost always uses relative paths).
3308
+ */
3309
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3310
+ (current_directory_len + len < max_path ))
3311
+ return len ;
3312
+
3313
+ /*
3314
+ * handle everything else:
3315
+ * - absolute paths: "C:\dir\file"
3316
+ * - absolute UNC paths: "\\server\share\dir\file"
3317
+ * - absolute paths on current drive: "\dir\file"
3318
+ * - relative paths on other drive: "X:file"
3319
+ * - prefixed paths: "\\?\...", "\\.\..."
3320
+ */
3321
+
3322
+ /* convert to absolute path using GetFullPathNameW */
3323
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3324
+ if (!result ) {
3325
+ errno = err_win_to_posix (GetLastError ());
3326
+ return -1 ;
3327
+ }
3328
+
3329
+ /*
3330
+ * return absolute path if it fits within max_path (even if
3331
+ * "cwd + path" doesn't due to '..' components)
3332
+ */
3333
+ if (result < max_path ) {
3334
+ wcscpy (path , buf );
3335
+ return result ;
3336
+ }
3337
+
3338
+ /* error out if we shouldn't expand the path or buf is too small */
3339
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3340
+ errno = ENAMETOOLONG ;
3341
+ return -1 ;
3342
+ }
3343
+
3344
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3345
+ if (buf [0 ] == '\\' ) {
3346
+ /* ...unless already prefixed */
3347
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3348
+ return len ;
3349
+
3350
+ wcscpy (path , L"\\\\?\\UNC\\" );
3351
+ wcscpy (path + 8 , buf + 2 );
3352
+ return result + 6 ;
3353
+ } else {
3354
+ wcscpy (path , L"\\\\?\\" );
3355
+ wcscpy (path + 4 , buf );
3356
+ return result + 4 ;
3357
+ }
3358
+ }
3359
+
3262
3360
#if !defined(_MSC_VER )
3263
3361
/*
3264
3362
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3420,6 +3518,9 @@ int wmain(int argc, const wchar_t **wargv)
3420
3518
/* initialize Unicode console */
3421
3519
winansi_init ();
3422
3520
3521
+ /* init length of current directory for handle_long_path */
3522
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3523
+
3423
3524
/* invoke the real main() using our utf8 version of argv. */
3424
3525
exit_status = main (argc , argv );
3425
3526
0 commit comments