@@ -228,6 +228,7 @@ enum hide_dotfiles_type {
228228static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY ;
229229static char * unset_environment_variables ;
230230int core_fscache ;
231+ int core_long_paths ;
231232
232233int mingw_core_config (const char * var , const char * value , void * cb )
233234{
@@ -244,6 +245,11 @@ int mingw_core_config(const char *var, const char *value, void *cb)
244245 return 0 ;
245246 }
246247
248+ if (!strcmp (var , "core.longpaths" )) {
249+ core_long_paths = git_config_bool (var , value );
250+ return 0 ;
251+ }
252+
247253 if (!strcmp (var , "core.unsetenvvars" )) {
248254 free (unset_environment_variables );
249255 unset_environment_variables = xstrdup (value );
@@ -281,8 +287,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
281287int mingw_unlink (const char * pathname )
282288{
283289 int ret , tries = 0 ;
284- wchar_t wpathname [MAX_PATH ];
285- if (xutftowcs_path (wpathname , pathname ) < 0 )
290+ wchar_t wpathname [MAX_LONG_PATH ];
291+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
286292 return -1 ;
287293
288294 /* read-only files cannot be removed */
@@ -311,7 +317,7 @@ static int is_dir_empty(const wchar_t *wpath)
311317{
312318 WIN32_FIND_DATAW findbuf ;
313319 HANDLE handle ;
314- wchar_t wbuf [MAX_PATH + 2 ];
320+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
315321 wcscpy (wbuf , wpath );
316322 wcscat (wbuf , L"\\*" );
317323 handle = FindFirstFileW (wbuf , & findbuf );
@@ -332,8 +338,8 @@ static int is_dir_empty(const wchar_t *wpath)
332338int mingw_rmdir (const char * pathname )
333339{
334340 int ret , tries = 0 ;
335- wchar_t wpathname [MAX_PATH ];
336- if (xutftowcs_path (wpathname , pathname ) < 0 )
341+ wchar_t wpathname [MAX_LONG_PATH ];
342+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
337343 return -1 ;
338344
339345 while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -408,9 +414,12 @@ static int set_hidden_flag(const wchar_t *path, int set)
408414int mingw_mkdir (const char * path , int mode )
409415{
410416 int ret ;
411- wchar_t wpath [MAX_PATH ];
412- if (xutftowcs_path (wpath , path ) < 0 )
417+ wchar_t wpath [MAX_LONG_PATH ];
418+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
419+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
420+ core_long_paths ) < 0 )
413421 return -1 ;
422+
414423 ret = _wmkdir (wpath );
415424 if (!ret && needs_hiding (path ))
416425 return set_hidden_flag (wpath , 1 );
@@ -483,7 +492,7 @@ int mingw_open (const char *filename, int oflags, ...)
483492 va_list args ;
484493 unsigned mode ;
485494 int fd ;
486- wchar_t wfilename [MAX_PATH ];
495+ wchar_t wfilename [MAX_LONG_PATH ];
487496 open_fn_t open_fn ;
488497
489498 va_start (args , oflags );
@@ -498,7 +507,7 @@ int mingw_open (const char *filename, int oflags, ...)
498507 else
499508 open_fn = _wopen ;
500509
501- if (xutftowcs_path (wfilename , filename ) < 0 )
510+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
502511 return -1 ;
503512 fd = open_fn (wfilename , oflags , mode );
504513
@@ -555,10 +564,10 @@ FILE *mingw_fopen (const char *filename, const char *otype)
555564{
556565 int hide = needs_hiding (filename );
557566 FILE * file ;
558- wchar_t wfilename [MAX_PATH ], wotype [4 ];
567+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
559568 if (filename && !strcmp (filename , "/dev/null" ))
560569 filename = "nul" ;
561- if (xutftowcs_path (wfilename , filename ) < 0 ||
570+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
562571 xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
563572 return NULL ;
564573 if (hide && !access (filename , F_OK ) && set_hidden_flag (wfilename , 0 )) {
@@ -577,10 +586,10 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
577586{
578587 int hide = needs_hiding (filename );
579588 FILE * file ;
580- wchar_t wfilename [MAX_PATH ], wotype [4 ];
589+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
581590 if (filename && !strcmp (filename , "/dev/null" ))
582591 filename = "nul" ;
583- if (xutftowcs_path (wfilename , filename ) < 0 ||
592+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
584593 xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
585594 return NULL ;
586595 if (hide && !access (filename , F_OK ) && set_hidden_flag (wfilename , 0 )) {
@@ -634,25 +643,31 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
634643
635644int mingw_access (const char * filename , int mode )
636645{
637- wchar_t wfilename [MAX_PATH ];
638- if (xutftowcs_path (wfilename , filename ) < 0 )
646+ wchar_t wfilename [MAX_LONG_PATH ];
647+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
639648 return -1 ;
640649 /* X_OK is not supported by the MSVCRT version */
641650 return _waccess (wfilename , mode & ~X_OK );
642651}
643652
653+ /* cached length of current directory for handle_long_path */
654+ static int current_directory_len = 0 ;
655+
644656int mingw_chdir (const char * dirname )
645657{
646- wchar_t wdirname [MAX_PATH ];
647- if (xutftowcs_path (wdirname , dirname ) < 0 )
658+ int result ;
659+ wchar_t wdirname [MAX_LONG_PATH ];
660+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
648661 return -1 ;
649- return _wchdir (wdirname );
662+ result = _wchdir (wdirname );
663+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
664+ return result ;
650665}
651666
652667int mingw_chmod (const char * filename , int mode )
653668{
654- wchar_t wfilename [MAX_PATH ];
655- if (xutftowcs_path (wfilename , filename ) < 0 )
669+ wchar_t wfilename [MAX_LONG_PATH ];
670+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
656671 return -1 ;
657672 return _wchmod (wfilename , mode );
658673}
@@ -700,8 +715,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
700715static int do_lstat (int follow , const char * file_name , struct stat * buf )
701716{
702717 WIN32_FILE_ATTRIBUTE_DATA fdata ;
703- wchar_t wfilename [MAX_PATH ];
704- if (xutftowcs_path (wfilename , file_name ) < 0 )
718+ wchar_t wfilename [MAX_LONG_PATH ];
719+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
705720 return -1 ;
706721
707722 if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -772,7 +787,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
772787static int do_stat_internal (int follow , const char * file_name , struct stat * buf )
773788{
774789 int namelen ;
775- char alt_name [PATH_MAX ];
790+ char alt_name [MAX_LONG_PATH ];
776791
777792 if (!do_lstat (follow , file_name , buf ))
778793 return 0 ;
@@ -788,7 +803,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
788803 return -1 ;
789804 while (namelen && file_name [namelen - 1 ] == '/' )
790805 -- namelen ;
791- if (!namelen || namelen >= PATH_MAX )
806+ if (!namelen || namelen >= MAX_LONG_PATH )
792807 return -1 ;
793808
794809 memcpy (alt_name , file_name , namelen );
@@ -872,8 +887,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
872887 FILETIME mft , aft ;
873888 int fh , rc ;
874889 DWORD attrs ;
875- wchar_t wfilename [MAX_PATH ];
876- if (xutftowcs_path (wfilename , file_name ) < 0 )
890+ wchar_t wfilename [MAX_LONG_PATH ];
891+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
877892 return -1 ;
878893
879894 /* must have write permission */
@@ -934,6 +949,7 @@ char *mingw_mktemp(char *template)
934949 wchar_t wtemplate [MAX_PATH ];
935950 int offset = 0 ;
936951
952+ /* we need to return the path, thus no long paths here! */
937953 if (xutftowcs_path (wtemplate , template ) < 0 )
938954 return NULL ;
939955
@@ -1455,6 +1471,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
14551471 si .hStdOutput = winansi_get_osfhandle (fhout );
14561472 si .hStdError = winansi_get_osfhandle (fherr );
14571473
1474+ /* executables and the current directory don't support long paths */
14581475 if (xutftowcs_path (wcmd , cmd ) < 0 )
14591476 return -1 ;
14601477 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -1860,8 +1877,9 @@ int mingw_rename(const char *pold, const char *pnew)
18601877{
18611878 DWORD attrs , gle ;
18621879 int tries = 0 ;
1863- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
1864- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
1880+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
1881+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
1882+ xutftowcs_long_path (wpnew , pnew ) < 0 )
18651883 return -1 ;
18661884
18671885 /*
@@ -2170,9 +2188,9 @@ int mingw_raise(int sig)
21702188
21712189int link (const char * oldpath , const char * newpath )
21722190{
2173- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2174- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2175- xutftowcs_path (wnewpath , newpath ) < 0 )
2191+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2192+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2193+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
21762194 return -1 ;
21772195
21782196 if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2348,6 +2366,68 @@ static void setup_windows_environment(void)
23482366 setenv ("TERM" , "cygwin" , 1 );
23492367}
23502368
2369+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
2370+ {
2371+ int result ;
2372+ wchar_t buf [MAX_LONG_PATH ];
2373+
2374+ /*
2375+ * we don't need special handling if path is relative to the current
2376+ * directory, and current directory + path don't exceed the desired
2377+ * max_path limit. This should cover > 99 % of cases with minimal
2378+ * performance impact (git almost always uses relative paths).
2379+ */
2380+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
2381+ (current_directory_len + len < max_path ))
2382+ return len ;
2383+
2384+ /*
2385+ * handle everything else:
2386+ * - absolute paths: "C:\dir\file"
2387+ * - absolute UNC paths: "\\server\share\dir\file"
2388+ * - absolute paths on current drive: "\dir\file"
2389+ * - relative paths on other drive: "X:file"
2390+ * - prefixed paths: "\\?\...", "\\.\..."
2391+ */
2392+
2393+ /* convert to absolute path using GetFullPathNameW */
2394+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
2395+ if (!result ) {
2396+ errno = err_win_to_posix (GetLastError ());
2397+ return -1 ;
2398+ }
2399+
2400+ /*
2401+ * return absolute path if it fits within max_path (even if
2402+ * "cwd + path" doesn't due to '..' components)
2403+ */
2404+ if (result < max_path ) {
2405+ wcscpy (path , buf );
2406+ return result ;
2407+ }
2408+
2409+ /* error out if we shouldn't expand the path or buf is too small */
2410+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
2411+ errno = ENAMETOOLONG ;
2412+ return -1 ;
2413+ }
2414+
2415+ /* prefix full path with "\\?\" or "\\?\UNC\" */
2416+ if (buf [0 ] == '\\' ) {
2417+ /* ...unless already prefixed */
2418+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
2419+ return len ;
2420+
2421+ wcscpy (path , L"\\\\?\\UNC\\" );
2422+ wcscpy (path + 8 , buf + 2 );
2423+ return result + 6 ;
2424+ } else {
2425+ wcscpy (path , L"\\\\?\\" );
2426+ wcscpy (path + 4 , buf );
2427+ return result + 4 ;
2428+ }
2429+ }
2430+
23512431#if !defined(_MSC_VER )
23522432/*
23532433 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -2505,6 +2585,9 @@ int wmain(int argc, const wchar_t **wargv)
25052585 /* initialize Unicode console */
25062586 winansi_init ();
25072587
2588+ /* init length of current directory for handle_long_path */
2589+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
2590+
25082591 /* invoke the real main() using our utf8 version of argv. */
25092592 exit_status = main (argc , argv );
25102593
0 commit comments