@@ -239,6 +239,27 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
239239static char * unset_environment_variables ;
240240int core_fscache ;
241241
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+
242263int mingw_core_config (const char * var , const char * value , void * cb )
243264{
244265 if (!strcmp (var , "core.hidedotfiles" )) {
@@ -300,8 +321,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
300321int mingw_unlink (const char * pathname )
301322{
302323 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 )
305326 return -1 ;
306327
307328 if (DeleteFileW (wpathname ))
@@ -333,7 +354,7 @@ static int is_dir_empty(const wchar_t *wpath)
333354{
334355 WIN32_FIND_DATAW findbuf ;
335356 HANDLE handle ;
336- wchar_t wbuf [MAX_PATH + 2 ];
357+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
337358 wcscpy (wbuf , wpath );
338359 wcscat (wbuf , L"\\*" );
339360 handle = FindFirstFileW (wbuf , & findbuf );
@@ -354,7 +375,7 @@ static int is_dir_empty(const wchar_t *wpath)
354375int mingw_rmdir (const char * pathname )
355376{
356377 int ret , tries = 0 ;
357- wchar_t wpathname [MAX_PATH ];
378+ wchar_t wpathname [MAX_LONG_PATH ];
358379 struct stat st ;
359380
360381 /*
@@ -376,7 +397,7 @@ int mingw_rmdir(const char *pathname)
376397 return -1 ;
377398 }
378399
379- if (xutftowcs_path (wpathname , pathname ) < 0 )
400+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
380401 return -1 ;
381402
382403 while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -455,15 +476,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
455476int mingw_mkdir (const char * path , int mode )
456477{
457478 int ret ;
458- wchar_t wpath [MAX_PATH ];
479+ wchar_t wpath [MAX_LONG_PATH ];
459480
460481 if (!is_valid_win32_path (path , 0 )) {
461482 errno = EINVAL ;
462483 return -1 ;
463484 }
464485
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 )
466489 return -1 ;
490+
467491 ret = _wmkdir (wpath );
468492 if (!ret && needs_hiding (path ))
469493 return set_hidden_flag (wpath , 1 );
@@ -550,7 +574,7 @@ int mingw_open (const char *filename, int oflags, ...)
550574 va_list args ;
551575 unsigned mode ;
552576 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 ];
554578 open_fn_t open_fn ;
555579
556580 va_start (args , oflags );
@@ -578,7 +602,7 @@ int mingw_open (const char *filename, int oflags, ...)
578602
579603 if (filename && !strcmp (filename , "/dev/null" ))
580604 wcscpy (wfilename , L"nul" );
581- else if (xutftowcs_path (wfilename , filename ) < 0 )
605+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
582606 return -1 ;
583607
584608 fd = open_fn (wfilename , oflags , mode );
@@ -636,14 +660,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
636660{
637661 int hide = needs_hiding (filename );
638662 FILE * file ;
639- wchar_t wfilename [MAX_PATH ], wotype [4 ];
663+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
640664 if (filename && !strcmp (filename , "/dev/null" ))
641665 wcscpy (wfilename , L"nul" );
642666 else if (!is_valid_win32_path (filename , 1 )) {
643667 int create = otype && strchr (otype , 'w' );
644668 errno = create ? EINVAL : ENOENT ;
645669 return NULL ;
646- } else if (xutftowcs_path (wfilename , filename ) < 0 )
670+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
647671 return NULL ;
648672
649673 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -665,14 +689,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
665689{
666690 int hide = needs_hiding (filename );
667691 FILE * file ;
668- wchar_t wfilename [MAX_PATH ], wotype [4 ];
692+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
669693 if (filename && !strcmp (filename , "/dev/null" ))
670694 wcscpy (wfilename , L"nul" );
671695 else if (!is_valid_win32_path (filename , 1 )) {
672696 int create = otype && strchr (otype , 'w' );
673697 errno = create ? EINVAL : ENOENT ;
674698 return NULL ;
675- } else if (xutftowcs_path (wfilename , filename ) < 0 )
699+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
676700 return NULL ;
677701
678702 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -747,27 +771,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
747771
748772int mingw_access (const char * filename , int mode )
749773{
750- wchar_t wfilename [MAX_PATH ];
774+ wchar_t wfilename [MAX_LONG_PATH ];
751775 if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
752776 return 0 ;
753- if (xutftowcs_path (wfilename , filename ) < 0 )
777+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
754778 return -1 ;
755779 /* X_OK is not supported by the MSVCRT version */
756780 return _waccess (wfilename , mode & ~X_OK );
757781}
758782
783+ /* cached length of current directory for handle_long_path */
784+ static int current_directory_len = 0 ;
785+
759786int mingw_chdir (const char * dirname )
760787{
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 )
763791 return -1 ;
764- return _wchdir (wdirname );
792+ result = _wchdir (wdirname );
793+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
794+ return result ;
765795}
766796
767797int mingw_chmod (const char * filename , int mode )
768798{
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 )
771801 return -1 ;
772802 return _wchmod (wfilename , mode );
773803}
@@ -815,8 +845,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
815845static int do_lstat (int follow , const char * file_name , struct stat * buf )
816846{
817847 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 )
820850 return -1 ;
821851
822852 if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -887,7 +917,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
887917static int do_stat_internal (int follow , const char * file_name , struct stat * buf )
888918{
889919 int namelen ;
890- char alt_name [PATH_MAX ];
920+ char alt_name [MAX_LONG_PATH ];
891921
892922 if (!do_lstat (follow , file_name , buf ))
893923 return 0 ;
@@ -903,7 +933,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
903933 return -1 ;
904934 while (namelen && file_name [namelen - 1 ] == '/' )
905935 -- namelen ;
906- if (!namelen || namelen >= PATH_MAX )
936+ if (!namelen || namelen >= MAX_LONG_PATH )
907937 return -1 ;
908938
909939 memcpy (alt_name , file_name , namelen );
@@ -987,10 +1017,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
9871017 FILETIME mft , aft ;
9881018 int rc ;
9891019 DWORD attrs ;
990- wchar_t wfilename [MAX_PATH ];
1020+ wchar_t wfilename [MAX_LONG_PATH ];
9911021 HANDLE osfilehandle ;
9921022
993- if (xutftowcs_path (wfilename , file_name ) < 0 )
1023+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
9941024 return -1 ;
9951025
9961026 /* must have write permission */
@@ -1073,6 +1103,7 @@ char *mingw_mktemp(char *template)
10731103 wchar_t wtemplate [MAX_PATH ];
10741104 int offset = 0 ;
10751105
1106+ /* we need to return the path, thus no long paths here! */
10761107 if (xutftowcs_path (wtemplate , template ) < 0 )
10771108 return NULL ;
10781109
@@ -1719,6 +1750,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
17191750
17201751 if (* argv && !strcmp (cmd , * argv ))
17211752 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+ */
17221757 else if (xutftowcs_path (wcmd , cmd ) < 0 )
17231758 return -1 ;
17241759 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2367,8 +2402,9 @@ int mingw_rename(const char *pold, const char *pnew)
23672402{
23682403 DWORD attrs , gle ;
23692404 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 )
23722408 return -1 ;
23732409
23742410 /*
@@ -2682,9 +2718,9 @@ int mingw_raise(int sig)
26822718
26832719int link (const char * oldpath , const char * newpath )
26842720{
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 )
26882724 return -1 ;
26892725
26902726 if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2752,8 +2788,8 @@ int mingw_is_mount_point(struct strbuf *path)
27522788{
27532789 WIN32_FIND_DATAW findbuf = { 0 };
27542790 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 );
27572793 if (wlen < 0 )
27582794 die (_ ("could not get long path for '%s'" ), path -> buf );
27592795
@@ -2898,9 +2934,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
28982934
28992935static int is_system32_path (const char * path )
29002936{
2901- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
2937+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
29022938
2903- if (xutftowcs_path (wpath , path ) < 0 ||
2939+ if (xutftowcs_long_path (wpath , path ) < 0 ||
29042940 !GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
29052941 _wcsicmp (system32 , wpath ))
29062942 return 0 ;
@@ -3268,6 +3304,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
32683304 }
32693305}
32703306
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+
32713369#if !defined(_MSC_VER )
32723370/*
32733371 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3429,6 +3527,9 @@ int wmain(int argc, const wchar_t **wargv)
34293527 /* initialize Unicode console */
34303528 winansi_init ();
34313529
3530+ /* init length of current directory for handle_long_path */
3531+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3532+
34323533 /* invoke the real main() using our utf8 version of argv. */
34333534 exit_status = main (argc , argv );
34343535
0 commit comments