@@ -252,6 +252,27 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
252252static char * unset_environment_variables ;
253253int core_fscache ;
254254
255+ int are_long_paths_enabled (void )
256+ {
257+ /* default to `false` during initialization */
258+ static const int fallback = 0 ;
259+
260+ static int enabled = -1 ;
261+
262+ if (enabled < 0 ) {
263+ /* avoid infinite recursion */
264+ if (!the_repository )
265+ return fallback ;
266+
267+ if (the_repository -> config &&
268+ the_repository -> config -> hash_initialized &&
269+ git_config_get_bool ("core.longpaths" , & enabled ) < 0 )
270+ enabled = 0 ;
271+ }
272+
273+ return enabled < 0 ? fallback : enabled ;
274+ }
275+
255276int mingw_core_config (const char * var , const char * value ,
256277 const struct config_context * ctx UNUSED ,
257278 void * cb UNUSED )
@@ -308,8 +329,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
308329int mingw_unlink (const char * pathname , int handle_in_use_error )
309330{
310331 int ret , tries = 0 ;
311- wchar_t wpathname [MAX_PATH ];
312- if (xutftowcs_path (wpathname , pathname ) < 0 )
332+ wchar_t wpathname [MAX_LONG_PATH ];
333+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
313334 return -1 ;
314335
315336 if (DeleteFileW (wpathname ))
@@ -344,7 +365,7 @@ static int is_dir_empty(const wchar_t *wpath)
344365{
345366 WIN32_FIND_DATAW findbuf ;
346367 HANDLE handle ;
347- wchar_t wbuf [MAX_PATH + 2 ];
368+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
348369 wcscpy (wbuf , wpath );
349370 wcscat (wbuf , L"\\*" );
350371 handle = FindFirstFileW (wbuf , & findbuf );
@@ -365,7 +386,7 @@ static int is_dir_empty(const wchar_t *wpath)
365386int mingw_rmdir (const char * pathname )
366387{
367388 int ret , tries = 0 ;
368- wchar_t wpathname [MAX_PATH ];
389+ wchar_t wpathname [MAX_LONG_PATH ];
369390 struct stat st ;
370391
371392 /*
@@ -387,7 +408,7 @@ int mingw_rmdir(const char *pathname)
387408 return -1 ;
388409 }
389410
390- if (xutftowcs_path (wpathname , pathname ) < 0 )
411+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
391412 return -1 ;
392413
393414 while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -466,15 +487,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
466487int mingw_mkdir (const char * path , int mode UNUSED )
467488{
468489 int ret ;
469- wchar_t wpath [MAX_PATH ];
490+ wchar_t wpath [MAX_LONG_PATH ];
470491
471492 if (!is_valid_win32_path (path , 0 )) {
472493 errno = EINVAL ;
473494 return -1 ;
474495 }
475496
476- if (xutftowcs_path (wpath , path ) < 0 )
497+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
498+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
499+ are_long_paths_enabled ()) < 0 )
477500 return -1 ;
501+
478502 ret = _wmkdir (wpath );
479503 if (!ret && needs_hiding (path ))
480504 return set_hidden_flag (wpath , 1 );
@@ -636,7 +660,7 @@ int mingw_open (const char *filename, int oflags, ...)
636660 va_list args ;
637661 unsigned mode ;
638662 int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
639- wchar_t wfilename [MAX_PATH ];
663+ wchar_t wfilename [MAX_LONG_PATH ];
640664 open_fn_t open_fn ;
641665
642666 DECLARE_PROC_ADDR (ntdll .dll , NTSTATUS , NTAPI , RtlGetLastNtStatus , void );
@@ -668,7 +692,7 @@ int mingw_open (const char *filename, int oflags, ...)
668692
669693 if (filename && !strcmp (filename , "/dev/null" ))
670694 wcscpy (wfilename , L"nul" );
671- else if (xutftowcs_path (wfilename , filename ) < 0 )
695+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
672696 return -1 ;
673697
674698 fd = open_fn (wfilename , oflags , mode );
@@ -741,14 +765,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
741765{
742766 int hide = needs_hiding (filename );
743767 FILE * file ;
744- wchar_t wfilename [MAX_PATH ], wotype [4 ];
768+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
745769 if (filename && !strcmp (filename , "/dev/null" ))
746770 wcscpy (wfilename , L"nul" );
747771 else if (!is_valid_win32_path (filename , 1 )) {
748772 int create = otype && strchr (otype , 'w' );
749773 errno = create ? EINVAL : ENOENT ;
750774 return NULL ;
751- } else if (xutftowcs_path (wfilename , filename ) < 0 )
775+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
752776 return NULL ;
753777
754778 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -770,14 +794,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
770794{
771795 int hide = needs_hiding (filename );
772796 FILE * file ;
773- wchar_t wfilename [MAX_PATH ], wotype [4 ];
797+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
774798 if (filename && !strcmp (filename , "/dev/null" ))
775799 wcscpy (wfilename , L"nul" );
776800 else if (!is_valid_win32_path (filename , 1 )) {
777801 int create = otype && strchr (otype , 'w' );
778802 errno = create ? EINVAL : ENOENT ;
779803 return NULL ;
780- } else if (xutftowcs_path (wfilename , filename ) < 0 )
804+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
781805 return NULL ;
782806
783807 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -827,7 +851,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
827851 HANDLE h = (HANDLE ) _get_osfhandle (fd );
828852 if (GetFileType (h ) != FILE_TYPE_PIPE ) {
829853 if (orig == EINVAL ) {
830- wchar_t path [MAX_PATH ];
854+ wchar_t path [MAX_LONG_PATH ];
831855 DWORD ret = GetFinalPathNameByHandleW (h , path ,
832856 ARRAY_SIZE (path ), 0 );
833857 UINT drive_type = ret > 0 && ret < ARRAY_SIZE (path ) ?
@@ -864,27 +888,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
864888
865889int mingw_access (const char * filename , int mode )
866890{
867- wchar_t wfilename [MAX_PATH ];
891+ wchar_t wfilename [MAX_LONG_PATH ];
868892 if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
869893 return 0 ;
870- if (xutftowcs_path (wfilename , filename ) < 0 )
894+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
871895 return -1 ;
872896 /* X_OK is not supported by the MSVCRT version */
873897 return _waccess (wfilename , mode & ~X_OK );
874898}
875899
900+ /* cached length of current directory for handle_long_path */
901+ static int current_directory_len = 0 ;
902+
876903int mingw_chdir (const char * dirname )
877904{
878- wchar_t wdirname [MAX_PATH ];
879- if (xutftowcs_path (wdirname , dirname ) < 0 )
905+ int result ;
906+ wchar_t wdirname [MAX_LONG_PATH ];
907+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
880908 return -1 ;
881- return _wchdir (wdirname );
909+ result = _wchdir (wdirname );
910+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
911+ return result ;
882912}
883913
884914int mingw_chmod (const char * filename , int mode )
885915{
886- wchar_t wfilename [MAX_PATH ];
887- if (xutftowcs_path (wfilename , filename ) < 0 )
916+ wchar_t wfilename [MAX_LONG_PATH ];
917+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
888918 return -1 ;
889919 return _wchmod (wfilename , mode );
890920}
@@ -932,8 +962,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
932962static int do_lstat (int follow , const char * file_name , struct stat * buf )
933963{
934964 WIN32_FILE_ATTRIBUTE_DATA fdata ;
935- wchar_t wfilename [MAX_PATH ];
936- if (xutftowcs_path (wfilename , file_name ) < 0 )
965+ wchar_t wfilename [MAX_LONG_PATH ];
966+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
937967 return -1 ;
938968
939969 if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -1104,10 +1134,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
11041134 FILETIME mft , aft ;
11051135 int rc ;
11061136 DWORD attrs ;
1107- wchar_t wfilename [MAX_PATH ];
1137+ wchar_t wfilename [MAX_LONG_PATH ];
11081138 HANDLE osfilehandle ;
11091139
1110- if (xutftowcs_path (wfilename , file_name ) < 0 )
1140+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
11111141 return -1 ;
11121142
11131143 /* must have write permission */
@@ -1190,6 +1220,7 @@ char *mingw_mktemp(char *template)
11901220 wchar_t wtemplate [MAX_PATH ];
11911221 int offset = 0 ;
11921222
1223+ /* we need to return the path, thus no long paths here! */
11931224 if (xutftowcs_path (wtemplate , template ) < 0 )
11941225 return NULL ;
11951226
@@ -1831,6 +1862,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
18311862
18321863 if (* argv && !strcmp (cmd , * argv ))
18331864 wcmd [0 ] = L'\0' ;
1865+ /*
1866+ * Paths to executables and to the current directory do not support
1867+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1868+ */
18341869 else if (xutftowcs_path (wcmd , cmd ) < 0 )
18351870 return -1 ;
18361871 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2520,12 +2555,12 @@ int mingw_rename(const char *pold, const char *pnew)
25202555 static int supports_file_rename_info_ex = 1 ;
25212556 DWORD attrs , gle ;
25222557 int tries = 0 ;
2523- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2558+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
25242559 int wpnew_len ;
25252560
2526- if (xutftowcs_path (wpold , pold ) < 0 )
2561+ if (xutftowcs_long_path (wpold , pold ) < 0 )
25272562 return -1 ;
2528- wpnew_len = xutftowcs_path (wpnew , pnew );
2563+ wpnew_len = xutftowcs_long_path (wpnew , pnew );
25292564 if (wpnew_len < 0 )
25302565 return -1 ;
25312566
@@ -2564,9 +2599,9 @@ int mingw_rename(const char *pold, const char *pnew)
25642599 * flex array so that the structure has to be allocated on
25652600 * the heap. As we declare this structure ourselves though
25662601 * we can avoid the allocation and define FileName to have
2567- * MAX_PATH bytes.
2602+ * MAX_LONG_PATH bytes.
25682603 */
2569- WCHAR FileName [MAX_PATH ];
2604+ WCHAR FileName [MAX_LONG_PATH ];
25702605 } rename_info = { 0 };
25712606 HANDLE old_handle = INVALID_HANDLE_VALUE ;
25722607 BOOL success ;
@@ -2919,9 +2954,9 @@ int mingw_raise(int sig)
29192954
29202955int link (const char * oldpath , const char * newpath )
29212956{
2922- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2923- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2924- xutftowcs_path (wnewpath , newpath ) < 0 )
2957+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2958+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2959+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
29252960 return -1 ;
29262961
29272962 if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2989,8 +3024,8 @@ int mingw_is_mount_point(struct strbuf *path)
29893024{
29903025 WIN32_FIND_DATAW findbuf = { 0 };
29913026 HANDLE handle ;
2992- wchar_t wfilename [MAX_PATH ];
2993- int wlen = xutftowcs_path (wfilename , path -> buf );
3027+ wchar_t wfilename [MAX_LONG_PATH ];
3028+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
29943029 if (wlen < 0 )
29953030 die (_ ("could not get long path for '%s'" ), path -> buf );
29963031
@@ -3142,9 +3177,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
31423177
31433178static int is_system32_path (const char * path )
31443179{
3145- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
3180+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
31463181
3147- if (xutftowcs_path (wpath , path ) < 0 ||
3182+ if (xutftowcs_long_path (wpath , path ) < 0 ||
31483183 !GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
31493184 _wcsicmp (system32 , wpath ))
31503185 return 0 ;
@@ -3577,6 +3612,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
35773612 }
35783613}
35793614
3615+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3616+ {
3617+ int result ;
3618+ wchar_t buf [MAX_LONG_PATH ];
3619+
3620+ /*
3621+ * we don't need special handling if path is relative to the current
3622+ * directory, and current directory + path don't exceed the desired
3623+ * max_path limit. This should cover > 99 % of cases with minimal
3624+ * performance impact (git almost always uses relative paths).
3625+ */
3626+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3627+ (current_directory_len + len < max_path ))
3628+ return len ;
3629+
3630+ /*
3631+ * handle everything else:
3632+ * - absolute paths: "C:\dir\file"
3633+ * - absolute UNC paths: "\\server\share\dir\file"
3634+ * - absolute paths on current drive: "\dir\file"
3635+ * - relative paths on other drive: "X:file"
3636+ * - prefixed paths: "\\?\...", "\\.\..."
3637+ */
3638+
3639+ /* convert to absolute path using GetFullPathNameW */
3640+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3641+ if (!result ) {
3642+ errno = err_win_to_posix (GetLastError ());
3643+ return -1 ;
3644+ }
3645+
3646+ /*
3647+ * return absolute path if it fits within max_path (even if
3648+ * "cwd + path" doesn't due to '..' components)
3649+ */
3650+ if (result < max_path ) {
3651+ wcscpy (path , buf );
3652+ return result ;
3653+ }
3654+
3655+ /* error out if we shouldn't expand the path or buf is too small */
3656+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3657+ errno = ENAMETOOLONG ;
3658+ return -1 ;
3659+ }
3660+
3661+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3662+ if (buf [0 ] == '\\' ) {
3663+ /* ...unless already prefixed */
3664+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3665+ return len ;
3666+
3667+ wcscpy (path , L"\\\\?\\UNC\\" );
3668+ wcscpy (path + 8 , buf + 2 );
3669+ return result + 6 ;
3670+ } else {
3671+ wcscpy (path , L"\\\\?\\" );
3672+ wcscpy (path + 4 , buf );
3673+ return result + 4 ;
3674+ }
3675+ }
3676+
35803677#if !defined(_MSC_VER )
35813678/*
35823679 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3739,6 +3836,9 @@ int wmain(int argc, const wchar_t **wargv)
37393836 /* initialize Unicode console */
37403837 winansi_init ();
37413838
3839+ /* init length of current directory for handle_long_path */
3840+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3841+
37423842 /* invoke the real main() using our utf8 version of argv. */
37433843 exit_status = main (argc , argv );
37443844
0 commit comments