@@ -251,6 +251,27 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
251251static char * unset_environment_variables ;
252252int core_fscache ;
253253
254+ int are_long_paths_enabled (void )
255+ {
256+ /* default to `false` during initialization */
257+ static const int fallback = 0 ;
258+
259+ static int enabled = -1 ;
260+
261+ if (enabled < 0 ) {
262+ /* avoid infinite recursion */
263+ if (!the_repository )
264+ return fallback ;
265+
266+ if (the_repository -> config &&
267+ the_repository -> config -> hash_initialized &&
268+ repo_config_get_bool (the_repository , "core.longpaths" , & enabled ) < 0 )
269+ enabled = 0 ;
270+ }
271+
272+ return enabled < 0 ? fallback : enabled ;
273+ }
274+
254275int mingw_core_config (const char * var , const char * value ,
255276 const struct config_context * ctx UNUSED ,
256277 void * cb UNUSED )
@@ -307,8 +328,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
307328int mingw_unlink (const char * pathname , int handle_in_use_error )
308329{
309330 int ret , tries = 0 ;
310- wchar_t wpathname [MAX_PATH ];
311- if (xutftowcs_path (wpathname , pathname ) < 0 )
331+ wchar_t wpathname [MAX_LONG_PATH ];
332+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
312333 return -1 ;
313334
314335 if (DeleteFileW (wpathname ))
@@ -343,7 +364,7 @@ static int is_dir_empty(const wchar_t *wpath)
343364{
344365 WIN32_FIND_DATAW findbuf ;
345366 HANDLE handle ;
346- wchar_t wbuf [MAX_PATH + 2 ];
367+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
347368 wcscpy (wbuf , wpath );
348369 wcscat (wbuf , L"\\*" );
349370 handle = FindFirstFileW (wbuf , & findbuf );
@@ -364,7 +385,7 @@ static int is_dir_empty(const wchar_t *wpath)
364385int mingw_rmdir (const char * pathname )
365386{
366387 int ret , tries = 0 ;
367- wchar_t wpathname [MAX_PATH ];
388+ wchar_t wpathname [MAX_LONG_PATH ];
368389 struct stat st ;
369390
370391 /*
@@ -386,7 +407,7 @@ int mingw_rmdir(const char *pathname)
386407 return -1 ;
387408 }
388409
389- if (xutftowcs_path (wpathname , pathname ) < 0 )
410+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
390411 return -1 ;
391412
392413 while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -465,15 +486,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
465486int mingw_mkdir (const char * path , int mode UNUSED )
466487{
467488 int ret ;
468- wchar_t wpath [MAX_PATH ];
489+ wchar_t wpath [MAX_LONG_PATH ];
469490
470491 if (!is_valid_win32_path (path , 0 )) {
471492 errno = EINVAL ;
472493 return -1 ;
473494 }
474495
475- if (xutftowcs_path (wpath , path ) < 0 )
496+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
497+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
498+ are_long_paths_enabled ()) < 0 )
476499 return -1 ;
500+
477501 ret = _wmkdir (wpath );
478502 if (!ret && needs_hiding (path ))
479503 return set_hidden_flag (wpath , 1 );
@@ -637,7 +661,7 @@ int mingw_open (const char *filename, int oflags, ...)
637661 va_list args ;
638662 unsigned mode ;
639663 int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
640- wchar_t wfilename [MAX_PATH ];
664+ wchar_t wfilename [MAX_LONG_PATH ];
641665 open_fn_t open_fn ;
642666
643667 DECLARE_PROC_ADDR (ntdll .dll , NTSTATUS , NTAPI , RtlGetLastNtStatus , void );
@@ -669,7 +693,7 @@ int mingw_open (const char *filename, int oflags, ...)
669693
670694 if (filename && !strcmp (filename , "/dev/null" ))
671695 wcscpy (wfilename , L"nul" );
672- else if (xutftowcs_path (wfilename , filename ) < 0 )
696+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
673697 return -1 ;
674698
675699 fd = open_fn (wfilename , oflags , mode );
@@ -742,14 +766,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
742766{
743767 int hide = needs_hiding (filename );
744768 FILE * file ;
745- wchar_t wfilename [MAX_PATH ], wotype [4 ];
769+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
746770 if (filename && !strcmp (filename , "/dev/null" ))
747771 wcscpy (wfilename , L"nul" );
748772 else if (!is_valid_win32_path (filename , 1 )) {
749773 int create = otype && strchr (otype , 'w' );
750774 errno = create ? EINVAL : ENOENT ;
751775 return NULL ;
752- } else if (xutftowcs_path (wfilename , filename ) < 0 )
776+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
753777 return NULL ;
754778
755779 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -771,14 +795,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
771795{
772796 int hide = needs_hiding (filename );
773797 FILE * file ;
774- wchar_t wfilename [MAX_PATH ], wotype [4 ];
798+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
775799 if (filename && !strcmp (filename , "/dev/null" ))
776800 wcscpy (wfilename , L"nul" );
777801 else if (!is_valid_win32_path (filename , 1 )) {
778802 int create = otype && strchr (otype , 'w' );
779803 errno = create ? EINVAL : ENOENT ;
780804 return NULL ;
781- } else if (xutftowcs_path (wfilename , filename ) < 0 )
805+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
782806 return NULL ;
783807
784808 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -828,7 +852,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
828852 HANDLE h = (HANDLE ) _get_osfhandle (fd );
829853 if (GetFileType (h ) != FILE_TYPE_PIPE ) {
830854 if (orig == EINVAL ) {
831- wchar_t path [MAX_PATH ];
855+ wchar_t path [MAX_LONG_PATH ];
832856 DWORD ret = GetFinalPathNameByHandleW (h , path ,
833857 ARRAY_SIZE (path ), 0 );
834858 UINT drive_type = ret > 0 && ret < ARRAY_SIZE (path ) ?
@@ -865,27 +889,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
865889
866890int mingw_access (const char * filename , int mode )
867891{
868- wchar_t wfilename [MAX_PATH ];
892+ wchar_t wfilename [MAX_LONG_PATH ];
869893 if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
870894 return 0 ;
871- if (xutftowcs_path (wfilename , filename ) < 0 )
895+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
872896 return -1 ;
873897 /* X_OK is not supported by the MSVCRT version */
874898 return _waccess (wfilename , mode & ~X_OK );
875899}
876900
901+ /* cached length of current directory for handle_long_path */
902+ static int current_directory_len = 0 ;
903+
877904int mingw_chdir (const char * dirname )
878905{
879- wchar_t wdirname [MAX_PATH ];
880- if (xutftowcs_path (wdirname , dirname ) < 0 )
906+ int result ;
907+ wchar_t wdirname [MAX_LONG_PATH ];
908+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
881909 return -1 ;
882- return _wchdir (wdirname );
910+ result = _wchdir (wdirname );
911+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
912+ return result ;
883913}
884914
885915int mingw_chmod (const char * filename , int mode )
886916{
887- wchar_t wfilename [MAX_PATH ];
888- if (xutftowcs_path (wfilename , filename ) < 0 )
917+ wchar_t wfilename [MAX_LONG_PATH ];
918+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
889919 return -1 ;
890920 return _wchmod (wfilename , mode );
891921}
@@ -933,8 +963,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
933963static int do_lstat (int follow , const char * file_name , struct stat * buf )
934964{
935965 WIN32_FILE_ATTRIBUTE_DATA fdata ;
936- wchar_t wfilename [MAX_PATH ];
937- if (xutftowcs_path (wfilename , file_name ) < 0 )
966+ wchar_t wfilename [MAX_LONG_PATH ];
967+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
938968 return -1 ;
939969
940970 if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -1105,10 +1135,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
11051135 FILETIME mft , aft ;
11061136 int rc ;
11071137 DWORD attrs ;
1108- wchar_t wfilename [MAX_PATH ];
1138+ wchar_t wfilename [MAX_LONG_PATH ];
11091139 HANDLE osfilehandle ;
11101140
1111- if (xutftowcs_path (wfilename , file_name ) < 0 )
1141+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
11121142 return -1 ;
11131143
11141144 /* must have write permission */
@@ -1191,6 +1221,7 @@ char *mingw_mktemp(char *template)
11911221 wchar_t wtemplate [MAX_PATH ];
11921222 int offset = 0 ;
11931223
1224+ /* we need to return the path, thus no long paths here! */
11941225 if (xutftowcs_path (wtemplate , template ) < 0 )
11951226 return NULL ;
11961227
@@ -1832,6 +1863,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
18321863
18331864 if (* argv && !strcmp (cmd , * argv ))
18341865 wcmd [0 ] = L'\0' ;
1866+ /*
1867+ * Paths to executables and to the current directory do not support
1868+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1869+ */
18351870 else if (xutftowcs_path (wcmd , cmd ) < 0 )
18361871 return -1 ;
18371872 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2521,12 +2556,12 @@ int mingw_rename(const char *pold, const char *pnew)
25212556 static int supports_file_rename_info_ex = 1 ;
25222557 DWORD attrs , gle ;
25232558 int tries = 0 ;
2524- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2559+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
25252560 int wpnew_len ;
25262561
2527- if (xutftowcs_path (wpold , pold ) < 0 )
2562+ if (xutftowcs_long_path (wpold , pold ) < 0 )
25282563 return -1 ;
2529- wpnew_len = xutftowcs_path (wpnew , pnew );
2564+ wpnew_len = xutftowcs_long_path (wpnew , pnew );
25302565 if (wpnew_len < 0 )
25312566 return -1 ;
25322567
@@ -2565,9 +2600,9 @@ int mingw_rename(const char *pold, const char *pnew)
25652600 * flex array so that the structure has to be allocated on
25662601 * the heap. As we declare this structure ourselves though
25672602 * we can avoid the allocation and define FileName to have
2568- * MAX_PATH bytes.
2603+ * MAX_LONG_PATH bytes.
25692604 */
2570- WCHAR FileName [MAX_PATH ];
2605+ WCHAR FileName [MAX_LONG_PATH ];
25712606 } rename_info = { 0 };
25722607 HANDLE old_handle = INVALID_HANDLE_VALUE ;
25732608 BOOL success ;
@@ -2930,9 +2965,9 @@ int mingw_raise(int sig)
29302965
29312966int link (const char * oldpath , const char * newpath )
29322967{
2933- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2934- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2935- xutftowcs_path (wnewpath , newpath ) < 0 )
2968+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2969+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2970+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
29362971 return -1 ;
29372972
29382973 if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -3000,8 +3035,8 @@ int mingw_is_mount_point(struct strbuf *path)
30003035{
30013036 WIN32_FIND_DATAW findbuf = { 0 };
30023037 HANDLE handle ;
3003- wchar_t wfilename [MAX_PATH ];
3004- int wlen = xutftowcs_path (wfilename , path -> buf );
3038+ wchar_t wfilename [MAX_LONG_PATH ];
3039+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
30053040 if (wlen < 0 )
30063041 die (_ ("could not get long path for '%s'" ), path -> buf );
30073042
@@ -3153,9 +3188,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
31533188
31543189static int is_system32_path (const char * path )
31553190{
3156- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
3191+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
31573192
3158- if (xutftowcs_path (wpath , path ) < 0 ||
3193+ if (xutftowcs_long_path (wpath , path ) < 0 ||
31593194 !GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
31603195 _wcsicmp (system32 , wpath ))
31613196 return 0 ;
@@ -3588,6 +3623,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
35883623 }
35893624}
35903625
3626+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3627+ {
3628+ int result ;
3629+ wchar_t buf [MAX_LONG_PATH ];
3630+
3631+ /*
3632+ * we don't need special handling if path is relative to the current
3633+ * directory, and current directory + path don't exceed the desired
3634+ * max_path limit. This should cover > 99 % of cases with minimal
3635+ * performance impact (git almost always uses relative paths).
3636+ */
3637+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3638+ (current_directory_len + len < max_path ))
3639+ return len ;
3640+
3641+ /*
3642+ * handle everything else:
3643+ * - absolute paths: "C:\dir\file"
3644+ * - absolute UNC paths: "\\server\share\dir\file"
3645+ * - absolute paths on current drive: "\dir\file"
3646+ * - relative paths on other drive: "X:file"
3647+ * - prefixed paths: "\\?\...", "\\.\..."
3648+ */
3649+
3650+ /* convert to absolute path using GetFullPathNameW */
3651+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3652+ if (!result ) {
3653+ errno = err_win_to_posix (GetLastError ());
3654+ return -1 ;
3655+ }
3656+
3657+ /*
3658+ * return absolute path if it fits within max_path (even if
3659+ * "cwd + path" doesn't due to '..' components)
3660+ */
3661+ if (result < max_path ) {
3662+ wcscpy (path , buf );
3663+ return result ;
3664+ }
3665+
3666+ /* error out if we shouldn't expand the path or buf is too small */
3667+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3668+ errno = ENAMETOOLONG ;
3669+ return -1 ;
3670+ }
3671+
3672+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3673+ if (buf [0 ] == '\\' ) {
3674+ /* ...unless already prefixed */
3675+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3676+ return len ;
3677+
3678+ wcscpy (path , L"\\\\?\\UNC\\" );
3679+ wcscpy (path + 8 , buf + 2 );
3680+ return result + 6 ;
3681+ } else {
3682+ wcscpy (path , L"\\\\?\\" );
3683+ wcscpy (path + 4 , buf );
3684+ return result + 4 ;
3685+ }
3686+ }
3687+
35913688#if !defined(_MSC_VER )
35923689/*
35933690 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3750,6 +3847,9 @@ int wmain(int argc, const wchar_t **wargv)
37503847 /* initialize Unicode console */
37513848 winansi_init ();
37523849
3850+ /* init length of current directory for handle_long_path */
3851+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3852+
37533853 /* invoke the real main() using our utf8 version of argv. */
37543854 exit_status = main (argc , argv );
37553855
0 commit comments