@@ -247,6 +247,27 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
247247static char * unset_environment_variables ;
248248int core_fscache ;
249249
250+ int are_long_paths_enabled (void )
251+ {
252+ /* default to `false` during initialization */
253+ static const int fallback = 0 ;
254+
255+ static int enabled = -1 ;
256+
257+ if (enabled < 0 ) {
258+ /* avoid infinite recursion */
259+ if (!the_repository )
260+ return fallback ;
261+
262+ if (the_repository -> config &&
263+ the_repository -> config -> hash_initialized &&
264+ git_config_get_bool ("core.longpaths" , & enabled ) < 0 )
265+ enabled = 0 ;
266+ }
267+
268+ return enabled < 0 ? fallback : enabled ;
269+ }
270+
250271int mingw_core_config (const char * var , const char * value ,
251272 const struct config_context * ctx UNUSED ,
252273 void * cb UNUSED )
@@ -312,8 +333,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
312333int mingw_unlink (const char * pathname )
313334{
314335 int ret , tries = 0 ;
315- wchar_t wpathname [MAX_PATH ];
316- if (xutftowcs_path (wpathname , pathname ) < 0 )
336+ wchar_t wpathname [MAX_LONG_PATH ];
337+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
317338 return -1 ;
318339
319340 if (DeleteFileW (wpathname ))
@@ -345,7 +366,7 @@ static int is_dir_empty(const wchar_t *wpath)
345366{
346367 WIN32_FIND_DATAW findbuf ;
347368 HANDLE handle ;
348- wchar_t wbuf [MAX_PATH + 2 ];
369+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
349370 wcscpy (wbuf , wpath );
350371 wcscat (wbuf , L"\\*" );
351372 handle = FindFirstFileW (wbuf , & findbuf );
@@ -366,7 +387,7 @@ static int is_dir_empty(const wchar_t *wpath)
366387int mingw_rmdir (const char * pathname )
367388{
368389 int ret , tries = 0 ;
369- wchar_t wpathname [MAX_PATH ];
390+ wchar_t wpathname [MAX_LONG_PATH ];
370391 struct stat st ;
371392
372393 /*
@@ -388,7 +409,7 @@ int mingw_rmdir(const char *pathname)
388409 return -1 ;
389410 }
390411
391- if (xutftowcs_path (wpathname , pathname ) < 0 )
412+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
392413 return -1 ;
393414
394415 while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -467,15 +488,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
467488int mingw_mkdir (const char * path , int mode UNUSED )
468489{
469490 int ret ;
470- wchar_t wpath [MAX_PATH ];
491+ wchar_t wpath [MAX_LONG_PATH ];
471492
472493 if (!is_valid_win32_path (path , 0 )) {
473494 errno = EINVAL ;
474495 return -1 ;
475496 }
476497
477- if (xutftowcs_path (wpath , path ) < 0 )
498+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
499+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
500+ are_long_paths_enabled ()) < 0 )
478501 return -1 ;
502+
479503 ret = _wmkdir (wpath );
480504 if (!ret && needs_hiding (path ))
481505 return set_hidden_flag (wpath , 1 );
@@ -562,7 +586,7 @@ int mingw_open (const char *filename, int oflags, ...)
562586 va_list args ;
563587 unsigned mode ;
564588 int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
565- wchar_t wfilename [MAX_PATH ];
589+ wchar_t wfilename [MAX_LONG_PATH ];
566590 open_fn_t open_fn ;
567591
568592 va_start (args , oflags );
@@ -590,7 +614,7 @@ int mingw_open (const char *filename, int oflags, ...)
590614
591615 if (filename && !strcmp (filename , "/dev/null" ))
592616 wcscpy (wfilename , L"nul" );
593- else if (xutftowcs_path (wfilename , filename ) < 0 )
617+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
594618 return -1 ;
595619
596620 fd = open_fn (wfilename , oflags , mode );
@@ -648,14 +672,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
648672{
649673 int hide = needs_hiding (filename );
650674 FILE * file ;
651- wchar_t wfilename [MAX_PATH ], wotype [4 ];
675+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
652676 if (filename && !strcmp (filename , "/dev/null" ))
653677 wcscpy (wfilename , L"nul" );
654678 else if (!is_valid_win32_path (filename , 1 )) {
655679 int create = otype && strchr (otype , 'w' );
656680 errno = create ? EINVAL : ENOENT ;
657681 return NULL ;
658- } else if (xutftowcs_path (wfilename , filename ) < 0 )
682+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
659683 return NULL ;
660684
661685 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -677,14 +701,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
677701{
678702 int hide = needs_hiding (filename );
679703 FILE * file ;
680- wchar_t wfilename [MAX_PATH ], wotype [4 ];
704+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
681705 if (filename && !strcmp (filename , "/dev/null" ))
682706 wcscpy (wfilename , L"nul" );
683707 else if (!is_valid_win32_path (filename , 1 )) {
684708 int create = otype && strchr (otype , 'w' );
685709 errno = create ? EINVAL : ENOENT ;
686710 return NULL ;
687- } else if (xutftowcs_path (wfilename , filename ) < 0 )
711+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
688712 return NULL ;
689713
690714 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -734,7 +758,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
734758 HANDLE h = (HANDLE ) _get_osfhandle (fd );
735759 if (GetFileType (h ) != FILE_TYPE_PIPE ) {
736760 if (orig == EINVAL ) {
737- wchar_t path [MAX_PATH ];
761+ wchar_t path [MAX_LONG_PATH ];
738762 DWORD ret = GetFinalPathNameByHandleW (h , path ,
739763 ARRAY_SIZE (path ), 0 );
740764 UINT drive_type = ret > 0 && ret < ARRAY_SIZE (path ) ?
@@ -771,27 +795,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
771795
772796int mingw_access (const char * filename , int mode )
773797{
774- wchar_t wfilename [MAX_PATH ];
798+ wchar_t wfilename [MAX_LONG_PATH ];
775799 if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
776800 return 0 ;
777- if (xutftowcs_path (wfilename , filename ) < 0 )
801+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
778802 return -1 ;
779803 /* X_OK is not supported by the MSVCRT version */
780804 return _waccess (wfilename , mode & ~X_OK );
781805}
782806
807+ /* cached length of current directory for handle_long_path */
808+ static int current_directory_len = 0 ;
809+
783810int mingw_chdir (const char * dirname )
784811{
785- wchar_t wdirname [MAX_PATH ];
786- if (xutftowcs_path (wdirname , dirname ) < 0 )
812+ int result ;
813+ wchar_t wdirname [MAX_LONG_PATH ];
814+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
787815 return -1 ;
788- return _wchdir (wdirname );
816+ result = _wchdir (wdirname );
817+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
818+ return result ;
789819}
790820
791821int mingw_chmod (const char * filename , int mode )
792822{
793- wchar_t wfilename [MAX_PATH ];
794- if (xutftowcs_path (wfilename , filename ) < 0 )
823+ wchar_t wfilename [MAX_LONG_PATH ];
824+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
795825 return -1 ;
796826 return _wchmod (wfilename , mode );
797827}
@@ -839,8 +869,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
839869static int do_lstat (int follow , const char * file_name , struct stat * buf )
840870{
841871 WIN32_FILE_ATTRIBUTE_DATA fdata ;
842- wchar_t wfilename [MAX_PATH ];
843- if (xutftowcs_path (wfilename , file_name ) < 0 )
872+ wchar_t wfilename [MAX_LONG_PATH ];
873+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
844874 return -1 ;
845875
846876 if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -1011,10 +1041,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
10111041 FILETIME mft , aft ;
10121042 int rc ;
10131043 DWORD attrs ;
1014- wchar_t wfilename [MAX_PATH ];
1044+ wchar_t wfilename [MAX_LONG_PATH ];
10151045 HANDLE osfilehandle ;
10161046
1017- if (xutftowcs_path (wfilename , file_name ) < 0 )
1047+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
10181048 return -1 ;
10191049
10201050 /* must have write permission */
@@ -1097,6 +1127,7 @@ char *mingw_mktemp(char *template)
10971127 wchar_t wtemplate [MAX_PATH ];
10981128 int offset = 0 ;
10991129
1130+ /* we need to return the path, thus no long paths here! */
11001131 if (xutftowcs_path (wtemplate , template ) < 0 )
11011132 return NULL ;
11021133
@@ -1748,6 +1779,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
17481779
17491780 if (* argv && !strcmp (cmd , * argv ))
17501781 wcmd [0 ] = L'\0' ;
1782+ /*
1783+ * Paths to executables and to the current directory do not support
1784+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1785+ */
17511786 else if (xutftowcs_path (wcmd , cmd ) < 0 )
17521787 return -1 ;
17531788 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2396,8 +2431,9 @@ int mingw_rename(const char *pold, const char *pnew)
23962431{
23972432 DWORD attrs , gle ;
23982433 int tries = 0 ;
2399- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2400- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2434+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2435+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2436+ xutftowcs_long_path (wpnew , pnew ) < 0 )
24012437 return -1 ;
24022438
24032439 /*
@@ -2715,9 +2751,9 @@ int mingw_raise(int sig)
27152751
27162752int link (const char * oldpath , const char * newpath )
27172753{
2718- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2719- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2720- xutftowcs_path (wnewpath , newpath ) < 0 )
2754+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2755+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2756+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
27212757 return -1 ;
27222758
27232759 if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2785,8 +2821,8 @@ int mingw_is_mount_point(struct strbuf *path)
27852821{
27862822 WIN32_FIND_DATAW findbuf = { 0 };
27872823 HANDLE handle ;
2788- wchar_t wfilename [MAX_PATH ];
2789- int wlen = xutftowcs_path (wfilename , path -> buf );
2824+ wchar_t wfilename [MAX_LONG_PATH ];
2825+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
27902826 if (wlen < 0 )
27912827 die (_ ("could not get long path for '%s'" ), path -> buf );
27922828
@@ -2931,9 +2967,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
29312967
29322968static int is_system32_path (const char * path )
29332969{
2934- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
2970+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
29352971
2936- if (xutftowcs_path (wpath , path ) < 0 ||
2972+ if (xutftowcs_long_path (wpath , path ) < 0 ||
29372973 !GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
29382974 _wcsicmp (system32 , wpath ))
29392975 return 0 ;
@@ -3345,6 +3381,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
33453381 }
33463382}
33473383
3384+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3385+ {
3386+ int result ;
3387+ wchar_t buf [MAX_LONG_PATH ];
3388+
3389+ /*
3390+ * we don't need special handling if path is relative to the current
3391+ * directory, and current directory + path don't exceed the desired
3392+ * max_path limit. This should cover > 99 % of cases with minimal
3393+ * performance impact (git almost always uses relative paths).
3394+ */
3395+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3396+ (current_directory_len + len < max_path ))
3397+ return len ;
3398+
3399+ /*
3400+ * handle everything else:
3401+ * - absolute paths: "C:\dir\file"
3402+ * - absolute UNC paths: "\\server\share\dir\file"
3403+ * - absolute paths on current drive: "\dir\file"
3404+ * - relative paths on other drive: "X:file"
3405+ * - prefixed paths: "\\?\...", "\\.\..."
3406+ */
3407+
3408+ /* convert to absolute path using GetFullPathNameW */
3409+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3410+ if (!result ) {
3411+ errno = err_win_to_posix (GetLastError ());
3412+ return -1 ;
3413+ }
3414+
3415+ /*
3416+ * return absolute path if it fits within max_path (even if
3417+ * "cwd + path" doesn't due to '..' components)
3418+ */
3419+ if (result < max_path ) {
3420+ wcscpy (path , buf );
3421+ return result ;
3422+ }
3423+
3424+ /* error out if we shouldn't expand the path or buf is too small */
3425+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3426+ errno = ENAMETOOLONG ;
3427+ return -1 ;
3428+ }
3429+
3430+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3431+ if (buf [0 ] == '\\' ) {
3432+ /* ...unless already prefixed */
3433+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3434+ return len ;
3435+
3436+ wcscpy (path , L"\\\\?\\UNC\\" );
3437+ wcscpy (path + 8 , buf + 2 );
3438+ return result + 6 ;
3439+ } else {
3440+ wcscpy (path , L"\\\\?\\" );
3441+ wcscpy (path + 4 , buf );
3442+ return result + 4 ;
3443+ }
3444+ }
3445+
33483446#if !defined(_MSC_VER )
33493447/*
33503448 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3506,6 +3604,9 @@ int wmain(int argc, const wchar_t **wargv)
35063604 /* initialize Unicode console */
35073605 winansi_init ();
35083606
3607+ /* init length of current directory for handle_long_path */
3608+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3609+
35093610 /* invoke the real main() using our utf8 version of argv. */
35103611 exit_status = main (argc , argv );
35113612
0 commit comments