@@ -248,6 +248,27 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
248248static char * unset_environment_variables ;
249249int core_fscache ;
250250
251+ int are_long_paths_enabled (void )
252+ {
253+ /* default to `false` during initialization */
254+ static const int fallback = 0 ;
255+
256+ static int enabled = -1 ;
257+
258+ if (enabled < 0 ) {
259+ /* avoid infinite recursion */
260+ if (!the_repository )
261+ return fallback ;
262+
263+ if (the_repository -> config &&
264+ the_repository -> config -> hash_initialized &&
265+ git_config_get_bool ("core.longpaths" , & enabled ) < 0 )
266+ enabled = 0 ;
267+ }
268+
269+ return enabled < 0 ? fallback : enabled ;
270+ }
271+
251272int mingw_core_config (const char * var , const char * value ,
252273 const struct config_context * ctx UNUSED ,
253274 void * cb UNUSED )
@@ -304,8 +325,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
304325int mingw_unlink (const char * pathname )
305326{
306327 int ret , tries = 0 ;
307- wchar_t wpathname [MAX_PATH ];
308- if (xutftowcs_path (wpathname , pathname ) < 0 )
328+ wchar_t wpathname [MAX_LONG_PATH ];
329+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
309330 return -1 ;
310331
311332 if (DeleteFileW (wpathname ))
@@ -337,7 +358,7 @@ static int is_dir_empty(const wchar_t *wpath)
337358{
338359 WIN32_FIND_DATAW findbuf ;
339360 HANDLE handle ;
340- wchar_t wbuf [MAX_PATH + 2 ];
361+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
341362 wcscpy (wbuf , wpath );
342363 wcscat (wbuf , L"\\*" );
343364 handle = FindFirstFileW (wbuf , & findbuf );
@@ -358,7 +379,7 @@ static int is_dir_empty(const wchar_t *wpath)
358379int mingw_rmdir (const char * pathname )
359380{
360381 int ret , tries = 0 ;
361- wchar_t wpathname [MAX_PATH ];
382+ wchar_t wpathname [MAX_LONG_PATH ];
362383 struct stat st ;
363384
364385 /*
@@ -380,7 +401,7 @@ int mingw_rmdir(const char *pathname)
380401 return -1 ;
381402 }
382403
383- if (xutftowcs_path (wpathname , pathname ) < 0 )
404+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
384405 return -1 ;
385406
386407 while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -459,15 +480,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
459480int mingw_mkdir (const char * path , int mode UNUSED )
460481{
461482 int ret ;
462- wchar_t wpath [MAX_PATH ];
483+ wchar_t wpath [MAX_LONG_PATH ];
463484
464485 if (!is_valid_win32_path (path , 0 )) {
465486 errno = EINVAL ;
466487 return -1 ;
467488 }
468489
469- if (xutftowcs_path (wpath , path ) < 0 )
490+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
491+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
492+ are_long_paths_enabled ()) < 0 )
470493 return -1 ;
494+
471495 ret = _wmkdir (wpath );
472496 if (!ret && needs_hiding (path ))
473497 return set_hidden_flag (wpath , 1 );
@@ -629,7 +653,7 @@ int mingw_open (const char *filename, int oflags, ...)
629653 va_list args ;
630654 unsigned mode ;
631655 int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
632- wchar_t wfilename [MAX_PATH ];
656+ wchar_t wfilename [MAX_LONG_PATH ];
633657 open_fn_t open_fn ;
634658
635659 va_start (args , oflags );
@@ -659,7 +683,7 @@ int mingw_open (const char *filename, int oflags, ...)
659683
660684 if (filename && !strcmp (filename , "/dev/null" ))
661685 wcscpy (wfilename , L"nul" );
662- else if (xutftowcs_path (wfilename , filename ) < 0 )
686+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
663687 return -1 ;
664688
665689 fd = open_fn (wfilename , oflags , mode );
@@ -717,14 +741,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
717741{
718742 int hide = needs_hiding (filename );
719743 FILE * file ;
720- wchar_t wfilename [MAX_PATH ], wotype [4 ];
744+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
721745 if (filename && !strcmp (filename , "/dev/null" ))
722746 wcscpy (wfilename , L"nul" );
723747 else if (!is_valid_win32_path (filename , 1 )) {
724748 int create = otype && strchr (otype , 'w' );
725749 errno = create ? EINVAL : ENOENT ;
726750 return NULL ;
727- } else if (xutftowcs_path (wfilename , filename ) < 0 )
751+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
728752 return NULL ;
729753
730754 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -746,14 +770,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
746770{
747771 int hide = needs_hiding (filename );
748772 FILE * file ;
749- wchar_t wfilename [MAX_PATH ], wotype [4 ];
773+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
750774 if (filename && !strcmp (filename , "/dev/null" ))
751775 wcscpy (wfilename , L"nul" );
752776 else if (!is_valid_win32_path (filename , 1 )) {
753777 int create = otype && strchr (otype , 'w' );
754778 errno = create ? EINVAL : ENOENT ;
755779 return NULL ;
756- } else if (xutftowcs_path (wfilename , filename ) < 0 )
780+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
757781 return NULL ;
758782
759783 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -803,7 +827,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
803827 HANDLE h = (HANDLE ) _get_osfhandle (fd );
804828 if (GetFileType (h ) != FILE_TYPE_PIPE ) {
805829 if (orig == EINVAL ) {
806- wchar_t path [MAX_PATH ];
830+ wchar_t path [MAX_LONG_PATH ];
807831 DWORD ret = GetFinalPathNameByHandleW (h , path ,
808832 ARRAY_SIZE (path ), 0 );
809833 UINT drive_type = ret > 0 && ret < ARRAY_SIZE (path ) ?
@@ -840,27 +864,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
840864
841865int mingw_access (const char * filename , int mode )
842866{
843- wchar_t wfilename [MAX_PATH ];
867+ wchar_t wfilename [MAX_LONG_PATH ];
844868 if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
845869 return 0 ;
846- if (xutftowcs_path (wfilename , filename ) < 0 )
870+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
847871 return -1 ;
848872 /* X_OK is not supported by the MSVCRT version */
849873 return _waccess (wfilename , mode & ~X_OK );
850874}
851875
876+ /* cached length of current directory for handle_long_path */
877+ static int current_directory_len = 0 ;
878+
852879int mingw_chdir (const char * dirname )
853880{
854- wchar_t wdirname [MAX_PATH ];
855- if (xutftowcs_path (wdirname , dirname ) < 0 )
881+ int result ;
882+ wchar_t wdirname [MAX_LONG_PATH ];
883+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
856884 return -1 ;
857- return _wchdir (wdirname );
885+ result = _wchdir (wdirname );
886+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
887+ return result ;
858888}
859889
860890int mingw_chmod (const char * filename , int mode )
861891{
862- wchar_t wfilename [MAX_PATH ];
863- if (xutftowcs_path (wfilename , filename ) < 0 )
892+ wchar_t wfilename [MAX_LONG_PATH ];
893+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
864894 return -1 ;
865895 return _wchmod (wfilename , mode );
866896}
@@ -908,8 +938,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
908938static int do_lstat (int follow , const char * file_name , struct stat * buf )
909939{
910940 WIN32_FILE_ATTRIBUTE_DATA fdata ;
911- wchar_t wfilename [MAX_PATH ];
912- if (xutftowcs_path (wfilename , file_name ) < 0 )
941+ wchar_t wfilename [MAX_LONG_PATH ];
942+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
913943 return -1 ;
914944
915945 if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -1080,10 +1110,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
10801110 FILETIME mft , aft ;
10811111 int rc ;
10821112 DWORD attrs ;
1083- wchar_t wfilename [MAX_PATH ];
1113+ wchar_t wfilename [MAX_LONG_PATH ];
10841114 HANDLE osfilehandle ;
10851115
1086- if (xutftowcs_path (wfilename , file_name ) < 0 )
1116+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
10871117 return -1 ;
10881118
10891119 /* must have write permission */
@@ -1166,6 +1196,7 @@ char *mingw_mktemp(char *template)
11661196 wchar_t wtemplate [MAX_PATH ];
11671197 int offset = 0 ;
11681198
1199+ /* we need to return the path, thus no long paths here! */
11691200 if (xutftowcs_path (wtemplate , template ) < 0 )
11701201 return NULL ;
11711202
@@ -1807,6 +1838,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
18071838
18081839 if (* argv && !strcmp (cmd , * argv ))
18091840 wcmd [0 ] = L'\0' ;
1841+ /*
1842+ * Paths to executables and to the current directory do not support
1843+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1844+ */
18101845 else if (xutftowcs_path (wcmd , cmd ) < 0 )
18111846 return -1 ;
18121847 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2496,12 +2531,12 @@ int mingw_rename(const char *pold, const char *pnew)
24962531 static int supports_file_rename_info_ex = 1 ;
24972532 DWORD attrs , gle ;
24982533 int tries = 0 ;
2499- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2534+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
25002535 int wpnew_len ;
25012536
2502- if (xutftowcs_path (wpold , pold ) < 0 )
2537+ if (xutftowcs_long_path (wpold , pold ) < 0 )
25032538 return -1 ;
2504- wpnew_len = xutftowcs_path (wpnew , pnew );
2539+ wpnew_len = xutftowcs_long_path (wpnew , pnew );
25052540 if (wpnew_len < 0 )
25062541 return -1 ;
25072542
@@ -2895,9 +2930,9 @@ int mingw_raise(int sig)
28952930
28962931int link (const char * oldpath , const char * newpath )
28972932{
2898- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2899- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2900- xutftowcs_path (wnewpath , newpath ) < 0 )
2933+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2934+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2935+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
29012936 return -1 ;
29022937
29032938 if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2965,8 +3000,8 @@ int mingw_is_mount_point(struct strbuf *path)
29653000{
29663001 WIN32_FIND_DATAW findbuf = { 0 };
29673002 HANDLE handle ;
2968- wchar_t wfilename [MAX_PATH ];
2969- int wlen = xutftowcs_path (wfilename , path -> buf );
3003+ wchar_t wfilename [MAX_LONG_PATH ];
3004+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
29703005 if (wlen < 0 )
29713006 die (_ ("could not get long path for '%s'" ), path -> buf );
29723007
@@ -3114,9 +3149,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
31143149
31153150static int is_system32_path (const char * path )
31163151{
3117- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
3152+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
31183153
3119- if (xutftowcs_path (wpath , path ) < 0 ||
3154+ if (xutftowcs_long_path (wpath , path ) < 0 ||
31203155 !GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
31213156 _wcsicmp (system32 , wpath ))
31223157 return 0 ;
@@ -3528,6 +3563,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
35283563 }
35293564}
35303565
3566+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3567+ {
3568+ int result ;
3569+ wchar_t buf [MAX_LONG_PATH ];
3570+
3571+ /*
3572+ * we don't need special handling if path is relative to the current
3573+ * directory, and current directory + path don't exceed the desired
3574+ * max_path limit. This should cover > 99 % of cases with minimal
3575+ * performance impact (git almost always uses relative paths).
3576+ */
3577+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3578+ (current_directory_len + len < max_path ))
3579+ return len ;
3580+
3581+ /*
3582+ * handle everything else:
3583+ * - absolute paths: "C:\dir\file"
3584+ * - absolute UNC paths: "\\server\share\dir\file"
3585+ * - absolute paths on current drive: "\dir\file"
3586+ * - relative paths on other drive: "X:file"
3587+ * - prefixed paths: "\\?\...", "\\.\..."
3588+ */
3589+
3590+ /* convert to absolute path using GetFullPathNameW */
3591+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3592+ if (!result ) {
3593+ errno = err_win_to_posix (GetLastError ());
3594+ return -1 ;
3595+ }
3596+
3597+ /*
3598+ * return absolute path if it fits within max_path (even if
3599+ * "cwd + path" doesn't due to '..' components)
3600+ */
3601+ if (result < max_path ) {
3602+ wcscpy (path , buf );
3603+ return result ;
3604+ }
3605+
3606+ /* error out if we shouldn't expand the path or buf is too small */
3607+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3608+ errno = ENAMETOOLONG ;
3609+ return -1 ;
3610+ }
3611+
3612+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3613+ if (buf [0 ] == '\\' ) {
3614+ /* ...unless already prefixed */
3615+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3616+ return len ;
3617+
3618+ wcscpy (path , L"\\\\?\\UNC\\" );
3619+ wcscpy (path + 8 , buf + 2 );
3620+ return result + 6 ;
3621+ } else {
3622+ wcscpy (path , L"\\\\?\\" );
3623+ wcscpy (path + 4 , buf );
3624+ return result + 4 ;
3625+ }
3626+ }
3627+
35313628#if !defined(_MSC_VER )
35323629/*
35333630 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3690,6 +3787,9 @@ int wmain(int argc, const wchar_t **wargv)
36903787 /* initialize Unicode console */
36913788 winansi_init ();
36923789
3790+ /* init length of current directory for handle_long_path */
3791+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3792+
36933793 /* invoke the real main() using our utf8 version of argv. */
36943794 exit_status = main (argc , argv );
36953795
0 commit comments