@@ -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
@@ -1707,6 +1738,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
17071738
17081739 if (* argv && !strcmp (cmd , * argv ))
17091740 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+ */
17101745 else if (xutftowcs_path (wcmd , cmd ) < 0 )
17111746 return -1 ;
17121747 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2358,8 +2393,9 @@ int mingw_rename(const char *pold, const char *pnew)
23582393{
23592394 DWORD attrs , gle ;
23602395 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 )
23632399 return -1 ;
23642400
23652401 /*
@@ -2673,9 +2709,9 @@ int mingw_raise(int sig)
26732709
26742710int link (const char * oldpath , const char * newpath )
26752711{
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 )
26792715 return -1 ;
26802716
26812717 if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2743,8 +2779,8 @@ int mingw_is_mount_point(struct strbuf *path)
27432779{
27442780 WIN32_FIND_DATAW findbuf = { 0 };
27452781 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 );
27482784 if (wlen < 0 )
27492785 die (_ ("could not get long path for '%s'" ), path -> buf );
27502786
@@ -2889,9 +2925,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
28892925
28902926static int is_system32_path (const char * path )
28912927{
2892- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
2928+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
28932929
2894- if (xutftowcs_path (wpath , path ) < 0 ||
2930+ if (xutftowcs_long_path (wpath , path ) < 0 ||
28952931 !GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
28962932 _wcsicmp (system32 , wpath ))
28972933 return 0 ;
@@ -3259,6 +3295,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
32593295 }
32603296}
32613297
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+
32623360#if !defined(_MSC_VER )
32633361/*
32643362 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3420,6 +3518,9 @@ int wmain(int argc, const wchar_t **wargv)
34203518 /* initialize Unicode console */
34213519 winansi_init ();
34223520
3521+ /* init length of current directory for handle_long_path */
3522+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3523+
34233524 /* invoke the real main() using our utf8 version of argv. */
34243525 exit_status = main (argc , argv );
34253526
0 commit comments