@@ -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 )
@@ -313,8 +334,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
313334int mingw_unlink (const char * pathname )
314335{
315336 int ret , tries = 0 ;
316- wchar_t wpathname [MAX_PATH ];
317- if (xutftowcs_path (wpathname , pathname ) < 0 )
337+ wchar_t wpathname [MAX_LONG_PATH ];
338+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
318339 return -1 ;
319340
320341 if (DeleteFileW (wpathname ))
@@ -346,7 +367,7 @@ static int is_dir_empty(const wchar_t *wpath)
346367{
347368 WIN32_FIND_DATAW findbuf ;
348369 HANDLE handle ;
349- wchar_t wbuf [MAX_PATH + 2 ];
370+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
350371 wcscpy (wbuf , wpath );
351372 wcscat (wbuf , L"\\*" );
352373 handle = FindFirstFileW (wbuf , & findbuf );
@@ -367,7 +388,7 @@ static int is_dir_empty(const wchar_t *wpath)
367388int mingw_rmdir (const char * pathname )
368389{
369390 int ret , tries = 0 ;
370- wchar_t wpathname [MAX_PATH ];
391+ wchar_t wpathname [MAX_LONG_PATH ];
371392 struct stat st ;
372393
373394 /*
@@ -389,7 +410,7 @@ int mingw_rmdir(const char *pathname)
389410 return -1 ;
390411 }
391412
392- if (xutftowcs_path (wpathname , pathname ) < 0 )
413+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
393414 return -1 ;
394415
395416 while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -468,15 +489,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
468489int mingw_mkdir (const char * path , int mode UNUSED )
469490{
470491 int ret ;
471- wchar_t wpath [MAX_PATH ];
492+ wchar_t wpath [MAX_LONG_PATH ];
472493
473494 if (!is_valid_win32_path (path , 0 )) {
474495 errno = EINVAL ;
475496 return -1 ;
476497 }
477498
478- if (xutftowcs_path (wpath , path ) < 0 )
499+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
500+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
501+ are_long_paths_enabled ()) < 0 )
479502 return -1 ;
503+
480504 ret = _wmkdir (wpath );
481505 if (!ret && needs_hiding (path ))
482506 return set_hidden_flag (wpath , 1 );
@@ -627,7 +651,7 @@ int mingw_open (const char *filename, int oflags, ...)
627651 va_list args ;
628652 unsigned mode ;
629653 int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
630- wchar_t wfilename [MAX_PATH ];
654+ wchar_t wfilename [MAX_LONG_PATH ];
631655 open_fn_t open_fn ;
632656
633657 va_start (args , oflags );
@@ -657,7 +681,7 @@ int mingw_open (const char *filename, int oflags, ...)
657681
658682 if (filename && !strcmp (filename , "/dev/null" ))
659683 wcscpy (wfilename , L"nul" );
660- else if (xutftowcs_path (wfilename , filename ) < 0 )
684+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
661685 return -1 ;
662686
663687 fd = open_fn (wfilename , oflags , mode );
@@ -715,14 +739,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
715739{
716740 int hide = needs_hiding (filename );
717741 FILE * file ;
718- wchar_t wfilename [MAX_PATH ], wotype [4 ];
742+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
719743 if (filename && !strcmp (filename , "/dev/null" ))
720744 wcscpy (wfilename , L"nul" );
721745 else if (!is_valid_win32_path (filename , 1 )) {
722746 int create = otype && strchr (otype , 'w' );
723747 errno = create ? EINVAL : ENOENT ;
724748 return NULL ;
725- } else if (xutftowcs_path (wfilename , filename ) < 0 )
749+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
726750 return NULL ;
727751
728752 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -744,14 +768,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
744768{
745769 int hide = needs_hiding (filename );
746770 FILE * file ;
747- wchar_t wfilename [MAX_PATH ], wotype [4 ];
771+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
748772 if (filename && !strcmp (filename , "/dev/null" ))
749773 wcscpy (wfilename , L"nul" );
750774 else if (!is_valid_win32_path (filename , 1 )) {
751775 int create = otype && strchr (otype , 'w' );
752776 errno = create ? EINVAL : ENOENT ;
753777 return NULL ;
754- } else if (xutftowcs_path (wfilename , filename ) < 0 )
778+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
755779 return NULL ;
756780
757781 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -801,7 +825,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
801825 HANDLE h = (HANDLE ) _get_osfhandle (fd );
802826 if (GetFileType (h ) != FILE_TYPE_PIPE ) {
803827 if (orig == EINVAL ) {
804- wchar_t path [MAX_PATH ];
828+ wchar_t path [MAX_LONG_PATH ];
805829 DWORD ret = GetFinalPathNameByHandleW (h , path ,
806830 ARRAY_SIZE (path ), 0 );
807831 UINT drive_type = ret > 0 && ret < ARRAY_SIZE (path ) ?
@@ -838,27 +862,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
838862
839863int mingw_access (const char * filename , int mode )
840864{
841- wchar_t wfilename [MAX_PATH ];
865+ wchar_t wfilename [MAX_LONG_PATH ];
842866 if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
843867 return 0 ;
844- if (xutftowcs_path (wfilename , filename ) < 0 )
868+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
845869 return -1 ;
846870 /* X_OK is not supported by the MSVCRT version */
847871 return _waccess (wfilename , mode & ~X_OK );
848872}
849873
874+ /* cached length of current directory for handle_long_path */
875+ static int current_directory_len = 0 ;
876+
850877int mingw_chdir (const char * dirname )
851878{
852- wchar_t wdirname [MAX_PATH ];
853- if (xutftowcs_path (wdirname , dirname ) < 0 )
879+ int result ;
880+ wchar_t wdirname [MAX_LONG_PATH ];
881+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
854882 return -1 ;
855- return _wchdir (wdirname );
883+ result = _wchdir (wdirname );
884+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
885+ return result ;
856886}
857887
858888int mingw_chmod (const char * filename , int mode )
859889{
860- wchar_t wfilename [MAX_PATH ];
861- if (xutftowcs_path (wfilename , filename ) < 0 )
890+ wchar_t wfilename [MAX_LONG_PATH ];
891+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
862892 return -1 ;
863893 return _wchmod (wfilename , mode );
864894}
@@ -906,8 +936,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
906936static int do_lstat (int follow , const char * file_name , struct stat * buf )
907937{
908938 WIN32_FILE_ATTRIBUTE_DATA fdata ;
909- wchar_t wfilename [MAX_PATH ];
910- if (xutftowcs_path (wfilename , file_name ) < 0 )
939+ wchar_t wfilename [MAX_LONG_PATH ];
940+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
911941 return -1 ;
912942
913943 if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -1078,10 +1108,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
10781108 FILETIME mft , aft ;
10791109 int rc ;
10801110 DWORD attrs ;
1081- wchar_t wfilename [MAX_PATH ];
1111+ wchar_t wfilename [MAX_LONG_PATH ];
10821112 HANDLE osfilehandle ;
10831113
1084- if (xutftowcs_path (wfilename , file_name ) < 0 )
1114+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
10851115 return -1 ;
10861116
10871117 /* must have write permission */
@@ -1164,6 +1194,7 @@ char *mingw_mktemp(char *template)
11641194 wchar_t wtemplate [MAX_PATH ];
11651195 int offset = 0 ;
11661196
1197+ /* we need to return the path, thus no long paths here! */
11671198 if (xutftowcs_path (wtemplate , template ) < 0 )
11681199 return NULL ;
11691200
@@ -1816,6 +1847,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
18161847
18171848 if (* argv && !strcmp (cmd , * argv ))
18181849 wcmd [0 ] = L'\0' ;
1850+ /*
1851+ * Paths to executables and to the current directory do not support
1852+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1853+ */
18191854 else if (xutftowcs_path (wcmd , cmd ) < 0 )
18201855 return -1 ;
18211856 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2468,12 +2503,12 @@ int mingw_rename(const char *pold, const char *pnew)
24682503 static int supports_file_rename_info_ex = 1 ;
24692504 DWORD attrs , gle ;
24702505 int tries = 0 ;
2471- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2506+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
24722507 int wpnew_len ;
24732508
2474- if (xutftowcs_path (wpold , pold ) < 0 )
2509+ if (xutftowcs_long_path (wpold , pold ) < 0 )
24752510 return -1 ;
2476- wpnew_len = xutftowcs_path (wpnew , pnew );
2511+ wpnew_len = xutftowcs_long_path (wpnew , pnew );
24772512 if (wpnew_len < 0 )
24782513 return -1 ;
24792514
@@ -2865,9 +2900,9 @@ int mingw_raise(int sig)
28652900
28662901int link (const char * oldpath , const char * newpath )
28672902{
2868- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2869- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2870- xutftowcs_path (wnewpath , newpath ) < 0 )
2903+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2904+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2905+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
28712906 return -1 ;
28722907
28732908 if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2935,8 +2970,8 @@ int mingw_is_mount_point(struct strbuf *path)
29352970{
29362971 WIN32_FIND_DATAW findbuf = { 0 };
29372972 HANDLE handle ;
2938- wchar_t wfilename [MAX_PATH ];
2939- int wlen = xutftowcs_path (wfilename , path -> buf );
2973+ wchar_t wfilename [MAX_LONG_PATH ];
2974+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
29402975 if (wlen < 0 )
29412976 die (_ ("could not get long path for '%s'" ), path -> buf );
29422977
@@ -3081,9 +3116,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
30813116
30823117static int is_system32_path (const char * path )
30833118{
3084- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
3119+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
30853120
3086- if (xutftowcs_path (wpath , path ) < 0 ||
3121+ if (xutftowcs_long_path (wpath , path ) < 0 ||
30873122 !GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
30883123 _wcsicmp (system32 , wpath ))
30893124 return 0 ;
@@ -3495,6 +3530,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
34953530 }
34963531}
34973532
3533+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3534+ {
3535+ int result ;
3536+ wchar_t buf [MAX_LONG_PATH ];
3537+
3538+ /*
3539+ * we don't need special handling if path is relative to the current
3540+ * directory, and current directory + path don't exceed the desired
3541+ * max_path limit. This should cover > 99 % of cases with minimal
3542+ * performance impact (git almost always uses relative paths).
3543+ */
3544+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3545+ (current_directory_len + len < max_path ))
3546+ return len ;
3547+
3548+ /*
3549+ * handle everything else:
3550+ * - absolute paths: "C:\dir\file"
3551+ * - absolute UNC paths: "\\server\share\dir\file"
3552+ * - absolute paths on current drive: "\dir\file"
3553+ * - relative paths on other drive: "X:file"
3554+ * - prefixed paths: "\\?\...", "\\.\..."
3555+ */
3556+
3557+ /* convert to absolute path using GetFullPathNameW */
3558+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3559+ if (!result ) {
3560+ errno = err_win_to_posix (GetLastError ());
3561+ return -1 ;
3562+ }
3563+
3564+ /*
3565+ * return absolute path if it fits within max_path (even if
3566+ * "cwd + path" doesn't due to '..' components)
3567+ */
3568+ if (result < max_path ) {
3569+ wcscpy (path , buf );
3570+ return result ;
3571+ }
3572+
3573+ /* error out if we shouldn't expand the path or buf is too small */
3574+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3575+ errno = ENAMETOOLONG ;
3576+ return -1 ;
3577+ }
3578+
3579+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3580+ if (buf [0 ] == '\\' ) {
3581+ /* ...unless already prefixed */
3582+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3583+ return len ;
3584+
3585+ wcscpy (path , L"\\\\?\\UNC\\" );
3586+ wcscpy (path + 8 , buf + 2 );
3587+ return result + 6 ;
3588+ } else {
3589+ wcscpy (path , L"\\\\?\\" );
3590+ wcscpy (path + 4 , buf );
3591+ return result + 4 ;
3592+ }
3593+ }
3594+
34983595#if !defined(_MSC_VER )
34993596/*
35003597 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3657,6 +3754,9 @@ int wmain(int argc, const wchar_t **wargv)
36573754 /* initialize Unicode console */
36583755 winansi_init ();
36593756
3757+ /* init length of current directory for handle_long_path */
3758+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3759+
36603760 /* invoke the real main() using our utf8 version of argv. */
36613761 exit_status = main (argc , argv );
36623762
0 commit comments