@@ -249,6 +249,27 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
249249static char * unset_environment_variables ;
250250int core_fscache ;
251251
252+ int are_long_paths_enabled (void )
253+ {
254+ /* default to `false` during initialization */
255+ static const int fallback = 0 ;
256+
257+ static int enabled = -1 ;
258+
259+ if (enabled < 0 ) {
260+ /* avoid infinite recursion */
261+ if (!the_repository )
262+ return fallback ;
263+
264+ if (the_repository -> config &&
265+ the_repository -> config -> hash_initialized &&
266+ git_config_get_bool ("core.longpaths" , & enabled ) < 0 )
267+ enabled = 0 ;
268+ }
269+
270+ return enabled < 0 ? fallback : enabled ;
271+ }
272+
252273int mingw_core_config (const char * var , const char * value ,
253274 const struct config_context * ctx UNUSED ,
254275 void * cb UNUSED )
@@ -314,8 +335,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
314335int mingw_unlink (const char * pathname )
315336{
316337 int ret , tries = 0 ;
317- wchar_t wpathname [MAX_PATH ];
318- if (xutftowcs_path (wpathname , pathname ) < 0 )
338+ wchar_t wpathname [MAX_LONG_PATH ];
339+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
319340 return -1 ;
320341
321342 if (DeleteFileW (wpathname ))
@@ -347,7 +368,7 @@ static int is_dir_empty(const wchar_t *wpath)
347368{
348369 WIN32_FIND_DATAW findbuf ;
349370 HANDLE handle ;
350- wchar_t wbuf [MAX_PATH + 2 ];
371+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
351372 wcscpy (wbuf , wpath );
352373 wcscat (wbuf , L"\\*" );
353374 handle = FindFirstFileW (wbuf , & findbuf );
@@ -368,7 +389,7 @@ static int is_dir_empty(const wchar_t *wpath)
368389int mingw_rmdir (const char * pathname )
369390{
370391 int ret , tries = 0 ;
371- wchar_t wpathname [MAX_PATH ];
392+ wchar_t wpathname [MAX_LONG_PATH ];
372393 struct stat st ;
373394
374395 /*
@@ -390,7 +411,7 @@ int mingw_rmdir(const char *pathname)
390411 return -1 ;
391412 }
392413
393- if (xutftowcs_path (wpathname , pathname ) < 0 )
414+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
394415 return -1 ;
395416
396417 while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -469,15 +490,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
469490int mingw_mkdir (const char * path , int mode UNUSED )
470491{
471492 int ret ;
472- wchar_t wpath [MAX_PATH ];
493+ wchar_t wpath [MAX_LONG_PATH ];
473494
474495 if (!is_valid_win32_path (path , 0 )) {
475496 errno = EINVAL ;
476497 return -1 ;
477498 }
478499
479- if (xutftowcs_path (wpath , path ) < 0 )
500+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
501+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
502+ are_long_paths_enabled ()) < 0 )
480503 return -1 ;
504+
481505 ret = _wmkdir (wpath );
482506 if (!ret && needs_hiding (path ))
483507 return set_hidden_flag (wpath , 1 );
@@ -628,7 +652,7 @@ int mingw_open (const char *filename, int oflags, ...)
628652 va_list args ;
629653 unsigned mode ;
630654 int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
631- wchar_t wfilename [MAX_PATH ];
655+ wchar_t wfilename [MAX_LONG_PATH ];
632656 open_fn_t open_fn ;
633657
634658 va_start (args , oflags );
@@ -658,7 +682,7 @@ int mingw_open (const char *filename, int oflags, ...)
658682
659683 if (filename && !strcmp (filename , "/dev/null" ))
660684 wcscpy (wfilename , L"nul" );
661- else if (xutftowcs_path (wfilename , filename ) < 0 )
685+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
662686 return -1 ;
663687
664688 fd = open_fn (wfilename , oflags , mode );
@@ -716,14 +740,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
716740{
717741 int hide = needs_hiding (filename );
718742 FILE * file ;
719- wchar_t wfilename [MAX_PATH ], wotype [4 ];
743+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
720744 if (filename && !strcmp (filename , "/dev/null" ))
721745 wcscpy (wfilename , L"nul" );
722746 else if (!is_valid_win32_path (filename , 1 )) {
723747 int create = otype && strchr (otype , 'w' );
724748 errno = create ? EINVAL : ENOENT ;
725749 return NULL ;
726- } else if (xutftowcs_path (wfilename , filename ) < 0 )
750+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
727751 return NULL ;
728752
729753 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -745,14 +769,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
745769{
746770 int hide = needs_hiding (filename );
747771 FILE * file ;
748- wchar_t wfilename [MAX_PATH ], wotype [4 ];
772+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
749773 if (filename && !strcmp (filename , "/dev/null" ))
750774 wcscpy (wfilename , L"nul" );
751775 else if (!is_valid_win32_path (filename , 1 )) {
752776 int create = otype && strchr (otype , 'w' );
753777 errno = create ? EINVAL : ENOENT ;
754778 return NULL ;
755- } else if (xutftowcs_path (wfilename , filename ) < 0 )
779+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
756780 return NULL ;
757781
758782 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -802,7 +826,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
802826 HANDLE h = (HANDLE ) _get_osfhandle (fd );
803827 if (GetFileType (h ) != FILE_TYPE_PIPE ) {
804828 if (orig == EINVAL ) {
805- wchar_t path [MAX_PATH ];
829+ wchar_t path [MAX_LONG_PATH ];
806830 DWORD ret = GetFinalPathNameByHandleW (h , path ,
807831 ARRAY_SIZE (path ), 0 );
808832 UINT drive_type = ret > 0 && ret < ARRAY_SIZE (path ) ?
@@ -839,27 +863,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
839863
840864int mingw_access (const char * filename , int mode )
841865{
842- wchar_t wfilename [MAX_PATH ];
866+ wchar_t wfilename [MAX_LONG_PATH ];
843867 if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
844868 return 0 ;
845- if (xutftowcs_path (wfilename , filename ) < 0 )
869+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
846870 return -1 ;
847871 /* X_OK is not supported by the MSVCRT version */
848872 return _waccess (wfilename , mode & ~X_OK );
849873}
850874
875+ /* cached length of current directory for handle_long_path */
876+ static int current_directory_len = 0 ;
877+
851878int mingw_chdir (const char * dirname )
852879{
853- wchar_t wdirname [MAX_PATH ];
854- if (xutftowcs_path (wdirname , dirname ) < 0 )
880+ int result ;
881+ wchar_t wdirname [MAX_LONG_PATH ];
882+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
855883 return -1 ;
856- return _wchdir (wdirname );
884+ result = _wchdir (wdirname );
885+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
886+ return result ;
857887}
858888
859889int mingw_chmod (const char * filename , int mode )
860890{
861- wchar_t wfilename [MAX_PATH ];
862- if (xutftowcs_path (wfilename , filename ) < 0 )
891+ wchar_t wfilename [MAX_LONG_PATH ];
892+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
863893 return -1 ;
864894 return _wchmod (wfilename , mode );
865895}
@@ -907,8 +937,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
907937static int do_lstat (int follow , const char * file_name , struct stat * buf )
908938{
909939 WIN32_FILE_ATTRIBUTE_DATA fdata ;
910- wchar_t wfilename [MAX_PATH ];
911- if (xutftowcs_path (wfilename , file_name ) < 0 )
940+ wchar_t wfilename [MAX_LONG_PATH ];
941+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
912942 return -1 ;
913943
914944 if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -1079,10 +1109,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
10791109 FILETIME mft , aft ;
10801110 int rc ;
10811111 DWORD attrs ;
1082- wchar_t wfilename [MAX_PATH ];
1112+ wchar_t wfilename [MAX_LONG_PATH ];
10831113 HANDLE osfilehandle ;
10841114
1085- if (xutftowcs_path (wfilename , file_name ) < 0 )
1115+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
10861116 return -1 ;
10871117
10881118 /* must have write permission */
@@ -1165,6 +1195,7 @@ char *mingw_mktemp(char *template)
11651195 wchar_t wtemplate [MAX_PATH ];
11661196 int offset = 0 ;
11671197
1198+ /* we need to return the path, thus no long paths here! */
11681199 if (xutftowcs_path (wtemplate , template ) < 0 )
11691200 return NULL ;
11701201
@@ -1817,6 +1848,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
18171848
18181849 if (* argv && !strcmp (cmd , * argv ))
18191850 wcmd [0 ] = L'\0' ;
1851+ /*
1852+ * Paths to executables and to the current directory do not support
1853+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1854+ */
18201855 else if (xutftowcs_path (wcmd , cmd ) < 0 )
18211856 return -1 ;
18221857 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2469,12 +2504,12 @@ int mingw_rename(const char *pold, const char *pnew)
24692504 static int supports_file_rename_info_ex = 1 ;
24702505 DWORD attrs , gle ;
24712506 int tries = 0 ;
2472- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2507+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
24732508 int wpnew_len ;
24742509
2475- if (xutftowcs_path (wpold , pold ) < 0 )
2510+ if (xutftowcs_long_path (wpold , pold ) < 0 )
24762511 return -1 ;
2477- wpnew_len = xutftowcs_path (wpnew , pnew );
2512+ wpnew_len = xutftowcs_long_path (wpnew , pnew );
24782513 if (wpnew_len < 0 )
24792514 return -1 ;
24802515
@@ -2866,9 +2901,9 @@ int mingw_raise(int sig)
28662901
28672902int link (const char * oldpath , const char * newpath )
28682903{
2869- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2870- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2871- xutftowcs_path (wnewpath , newpath ) < 0 )
2904+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2905+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2906+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
28722907 return -1 ;
28732908
28742909 if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2936,8 +2971,8 @@ int mingw_is_mount_point(struct strbuf *path)
29362971{
29372972 WIN32_FIND_DATAW findbuf = { 0 };
29382973 HANDLE handle ;
2939- wchar_t wfilename [MAX_PATH ];
2940- int wlen = xutftowcs_path (wfilename , path -> buf );
2974+ wchar_t wfilename [MAX_LONG_PATH ];
2975+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
29412976 if (wlen < 0 )
29422977 die (_ ("could not get long path for '%s'" ), path -> buf );
29432978
@@ -3082,9 +3117,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
30823117
30833118static int is_system32_path (const char * path )
30843119{
3085- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
3120+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
30863121
3087- if (xutftowcs_path (wpath , path ) < 0 ||
3122+ if (xutftowcs_long_path (wpath , path ) < 0 ||
30883123 !GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
30893124 _wcsicmp (system32 , wpath ))
30903125 return 0 ;
@@ -3496,6 +3531,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
34963531 }
34973532}
34983533
3534+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3535+ {
3536+ int result ;
3537+ wchar_t buf [MAX_LONG_PATH ];
3538+
3539+ /*
3540+ * we don't need special handling if path is relative to the current
3541+ * directory, and current directory + path don't exceed the desired
3542+ * max_path limit. This should cover > 99 % of cases with minimal
3543+ * performance impact (git almost always uses relative paths).
3544+ */
3545+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3546+ (current_directory_len + len < max_path ))
3547+ return len ;
3548+
3549+ /*
3550+ * handle everything else:
3551+ * - absolute paths: "C:\dir\file"
3552+ * - absolute UNC paths: "\\server\share\dir\file"
3553+ * - absolute paths on current drive: "\dir\file"
3554+ * - relative paths on other drive: "X:file"
3555+ * - prefixed paths: "\\?\...", "\\.\..."
3556+ */
3557+
3558+ /* convert to absolute path using GetFullPathNameW */
3559+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3560+ if (!result ) {
3561+ errno = err_win_to_posix (GetLastError ());
3562+ return -1 ;
3563+ }
3564+
3565+ /*
3566+ * return absolute path if it fits within max_path (even if
3567+ * "cwd + path" doesn't due to '..' components)
3568+ */
3569+ if (result < max_path ) {
3570+ wcscpy (path , buf );
3571+ return result ;
3572+ }
3573+
3574+ /* error out if we shouldn't expand the path or buf is too small */
3575+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3576+ errno = ENAMETOOLONG ;
3577+ return -1 ;
3578+ }
3579+
3580+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3581+ if (buf [0 ] == '\\' ) {
3582+ /* ...unless already prefixed */
3583+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3584+ return len ;
3585+
3586+ wcscpy (path , L"\\\\?\\UNC\\" );
3587+ wcscpy (path + 8 , buf + 2 );
3588+ return result + 6 ;
3589+ } else {
3590+ wcscpy (path , L"\\\\?\\" );
3591+ wcscpy (path + 4 , buf );
3592+ return result + 4 ;
3593+ }
3594+ }
3595+
34993596#if !defined(_MSC_VER )
35003597/*
35013598 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3658,6 +3755,9 @@ int wmain(int argc, const wchar_t **wargv)
36583755 /* initialize Unicode console */
36593756 winansi_init ();
36603757
3758+ /* init length of current directory for handle_long_path */
3759+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3760+
36613761 /* invoke the real main() using our utf8 version of argv. */
36623762 exit_status = main (argc , argv );
36633763
0 commit comments