@@ -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
@@ -1719,6 +1750,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1719
1750
1720
1751
if (* argv && !strcmp (cmd , * argv ))
1721
1752
wcmd [0 ] = L'\0' ;
1753
+ /*
1754
+ * Paths to executables and to the current directory do not support
1755
+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1756
+ */
1722
1757
else if (xutftowcs_path (wcmd , cmd ) < 0 )
1723
1758
return -1 ;
1724
1759
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2367,8 +2402,9 @@ int mingw_rename(const char *pold, const char *pnew)
2367
2402
{
2368
2403
DWORD attrs , gle ;
2369
2404
int tries = 0 ;
2370
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2371
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2405
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2406
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2407
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
2372
2408
return -1 ;
2373
2409
2374
2410
/*
@@ -2682,9 +2718,9 @@ int mingw_raise(int sig)
2682
2718
2683
2719
int link (const char * oldpath , const char * newpath )
2684
2720
{
2685
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2686
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2687
- xutftowcs_path (wnewpath , newpath ) < 0 )
2721
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2722
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2723
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2688
2724
return -1 ;
2689
2725
2690
2726
if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2752,8 +2788,8 @@ int mingw_is_mount_point(struct strbuf *path)
2752
2788
{
2753
2789
WIN32_FIND_DATAW findbuf = { 0 };
2754
2790
HANDLE handle ;
2755
- wchar_t wfilename [MAX_PATH ];
2756
- int wlen = xutftowcs_path (wfilename , path -> buf );
2791
+ wchar_t wfilename [MAX_LONG_PATH ];
2792
+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
2757
2793
if (wlen < 0 )
2758
2794
die (_ ("could not get long path for '%s'" ), path -> buf );
2759
2795
@@ -2898,9 +2934,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
2898
2934
2899
2935
static int is_system32_path (const char * path )
2900
2936
{
2901
- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
2937
+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
2902
2938
2903
- if (xutftowcs_path (wpath , path ) < 0 ||
2939
+ if (xutftowcs_long_path (wpath , path ) < 0 ||
2904
2940
!GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
2905
2941
_wcsicmp (system32 , wpath ))
2906
2942
return 0 ;
@@ -3268,6 +3304,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
3268
3304
}
3269
3305
}
3270
3306
3307
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3308
+ {
3309
+ int result ;
3310
+ wchar_t buf [MAX_LONG_PATH ];
3311
+
3312
+ /*
3313
+ * we don't need special handling if path is relative to the current
3314
+ * directory, and current directory + path don't exceed the desired
3315
+ * max_path limit. This should cover > 99 % of cases with minimal
3316
+ * performance impact (git almost always uses relative paths).
3317
+ */
3318
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3319
+ (current_directory_len + len < max_path ))
3320
+ return len ;
3321
+
3322
+ /*
3323
+ * handle everything else:
3324
+ * - absolute paths: "C:\dir\file"
3325
+ * - absolute UNC paths: "\\server\share\dir\file"
3326
+ * - absolute paths on current drive: "\dir\file"
3327
+ * - relative paths on other drive: "X:file"
3328
+ * - prefixed paths: "\\?\...", "\\.\..."
3329
+ */
3330
+
3331
+ /* convert to absolute path using GetFullPathNameW */
3332
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3333
+ if (!result ) {
3334
+ errno = err_win_to_posix (GetLastError ());
3335
+ return -1 ;
3336
+ }
3337
+
3338
+ /*
3339
+ * return absolute path if it fits within max_path (even if
3340
+ * "cwd + path" doesn't due to '..' components)
3341
+ */
3342
+ if (result < max_path ) {
3343
+ wcscpy (path , buf );
3344
+ return result ;
3345
+ }
3346
+
3347
+ /* error out if we shouldn't expand the path or buf is too small */
3348
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3349
+ errno = ENAMETOOLONG ;
3350
+ return -1 ;
3351
+ }
3352
+
3353
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3354
+ if (buf [0 ] == '\\' ) {
3355
+ /* ...unless already prefixed */
3356
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3357
+ return len ;
3358
+
3359
+ wcscpy (path , L"\\\\?\\UNC\\" );
3360
+ wcscpy (path + 8 , buf + 2 );
3361
+ return result + 6 ;
3362
+ } else {
3363
+ wcscpy (path , L"\\\\?\\" );
3364
+ wcscpy (path + 4 , buf );
3365
+ return result + 4 ;
3366
+ }
3367
+ }
3368
+
3271
3369
#if !defined(_MSC_VER )
3272
3370
/*
3273
3371
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3429,6 +3527,9 @@ int wmain(int argc, const wchar_t **wargv)
3429
3527
/* initialize Unicode console */
3430
3528
winansi_init ();
3431
3529
3530
+ /* init length of current directory for handle_long_path */
3531
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3532
+
3432
3533
/* invoke the real main() using our utf8 version of argv. */
3433
3534
exit_status = main (argc , argv );
3434
3535
0 commit comments