@@ -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
@@ -1457,6 +1473,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
14571473
14581474 if (* argv && !strcmp (cmd , * argv ))
14591475 wcmd [0 ] = L'\0' ;
1476+ /*
1477+ * Paths to executables and to the current directory do not support
1478+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1479+ */
14601480 else if (xutftowcs_path (wcmd , cmd ) < 0 )
14611481 return -1 ;
14621482 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -1864,8 +1884,9 @@ int mingw_rename(const char *pold, const char *pnew)
18641884{
18651885 DWORD attrs , gle ;
18661886 int tries = 0 ;
1867- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
1868- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
1887+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
1888+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
1889+ xutftowcs_long_path (wpnew , pnew ) < 0 )
18691890 return -1 ;
18701891
18711892 /*
@@ -2174,9 +2195,9 @@ int mingw_raise(int sig)
21742195
21752196int link (const char * oldpath , const char * newpath )
21762197{
2177- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2178- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2179- xutftowcs_path (wnewpath , newpath ) < 0 )
2198+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2199+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2200+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
21802201 return -1 ;
21812202
21822203 if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2376,6 +2397,68 @@ static void setup_windows_environment(void)
23762397 }
23772398}
23782399
2400+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
2401+ {
2402+ int result ;
2403+ wchar_t buf [MAX_LONG_PATH ];
2404+
2405+ /*
2406+ * we don't need special handling if path is relative to the current
2407+ * directory, and current directory + path don't exceed the desired
2408+ * max_path limit. This should cover > 99 % of cases with minimal
2409+ * performance impact (git almost always uses relative paths).
2410+ */
2411+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
2412+ (current_directory_len + len < max_path ))
2413+ return len ;
2414+
2415+ /*
2416+ * handle everything else:
2417+ * - absolute paths: "C:\dir\file"
2418+ * - absolute UNC paths: "\\server\share\dir\file"
2419+ * - absolute paths on current drive: "\dir\file"
2420+ * - relative paths on other drive: "X:file"
2421+ * - prefixed paths: "\\?\...", "\\.\..."
2422+ */
2423+
2424+ /* convert to absolute path using GetFullPathNameW */
2425+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
2426+ if (!result ) {
2427+ errno = err_win_to_posix (GetLastError ());
2428+ return -1 ;
2429+ }
2430+
2431+ /*
2432+ * return absolute path if it fits within max_path (even if
2433+ * "cwd + path" doesn't due to '..' components)
2434+ */
2435+ if (result < max_path ) {
2436+ wcscpy (path , buf );
2437+ return result ;
2438+ }
2439+
2440+ /* error out if we shouldn't expand the path or buf is too small */
2441+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
2442+ errno = ENAMETOOLONG ;
2443+ return -1 ;
2444+ }
2445+
2446+ /* prefix full path with "\\?\" or "\\?\UNC\" */
2447+ if (buf [0 ] == '\\' ) {
2448+ /* ...unless already prefixed */
2449+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
2450+ return len ;
2451+
2452+ wcscpy (path , L"\\\\?\\UNC\\" );
2453+ wcscpy (path + 8 , buf + 2 );
2454+ return result + 6 ;
2455+ } else {
2456+ wcscpy (path , L"\\\\?\\" );
2457+ wcscpy (path + 4 , buf );
2458+ return result + 4 ;
2459+ }
2460+ }
2461+
23792462#if !defined(_MSC_VER )
23802463/*
23812464 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -2534,6 +2617,9 @@ int wmain(int argc, const wchar_t **wargv)
25342617 /* initialize Unicode console */
25352618 winansi_init ();
25362619
2620+ /* init length of current directory for handle_long_path */
2621+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
2622+
25372623 /* invoke the real main() using our utf8 version of argv. */
25382624 exit_status = main (argc , argv );
25392625
0 commit comments