@@ -213,6 +213,7 @@ enum hide_dotfiles_type {
213213static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY ;
214214static char * unset_environment_variables ;
215215int core_fscache ;
216+ int core_long_paths ;
216217
217218int mingw_core_config (const char * var , const char * value , void * cb )
218219{
@@ -229,6 +230,11 @@ int mingw_core_config(const char *var, const char *value, void *cb)
229230 return 0 ;
230231 }
231232
233+ if (!strcmp (var , "core.longpaths" )) {
234+ core_long_paths = git_config_bool (var , value );
235+ return 0 ;
236+ }
237+
232238 if (!strcmp (var , "core.unsetenvvars" )) {
233239 free (unset_environment_variables );
234240 unset_environment_variables = xstrdup (value );
@@ -266,8 +272,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
266272int mingw_unlink (const char * pathname )
267273{
268274 int ret , tries = 0 ;
269- wchar_t wpathname [MAX_PATH ];
270- if (xutftowcs_path (wpathname , pathname ) < 0 )
275+ wchar_t wpathname [MAX_LONG_PATH ];
276+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
271277 return -1 ;
272278
273279 /* read-only files cannot be removed */
@@ -296,7 +302,7 @@ static int is_dir_empty(const wchar_t *wpath)
296302{
297303 WIN32_FIND_DATAW findbuf ;
298304 HANDLE handle ;
299- wchar_t wbuf [MAX_PATH + 2 ];
305+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
300306 wcscpy (wbuf , wpath );
301307 wcscat (wbuf , L"\\*" );
302308 handle = FindFirstFileW (wbuf , & findbuf );
@@ -317,8 +323,8 @@ static int is_dir_empty(const wchar_t *wpath)
317323int mingw_rmdir (const char * pathname )
318324{
319325 int ret , tries = 0 ;
320- wchar_t wpathname [MAX_PATH ];
321- if (xutftowcs_path (wpathname , pathname ) < 0 )
326+ wchar_t wpathname [MAX_LONG_PATH ];
327+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
322328 return -1 ;
323329
324330 while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -393,9 +399,12 @@ static int set_hidden_flag(const wchar_t *path, int set)
393399int mingw_mkdir (const char * path , int mode )
394400{
395401 int ret ;
396- wchar_t wpath [MAX_PATH ];
397- if (xutftowcs_path (wpath , path ) < 0 )
402+ wchar_t wpath [MAX_LONG_PATH ];
403+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
404+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
405+ core_long_paths ) < 0 )
398406 return -1 ;
407+
399408 ret = _wmkdir (wpath );
400409 if (!ret && needs_hiding (path ))
401410 return set_hidden_flag (wpath , 1 );
@@ -438,7 +447,7 @@ int mingw_open (const char *filename, int oflags, ...)
438447 va_list args ;
439448 unsigned mode ;
440449 int fd ;
441- wchar_t wfilename [MAX_PATH ];
450+ wchar_t wfilename [MAX_LONG_PATH ];
442451 open_fn_t open_fn ;
443452
444453 va_start (args , oflags );
@@ -453,7 +462,7 @@ int mingw_open (const char *filename, int oflags, ...)
453462 else
454463 open_fn = _wopen ;
455464
456- if (xutftowcs_path (wfilename , filename ) < 0 )
465+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
457466 return -1 ;
458467 fd = open_fn (wfilename , oflags , mode );
459468
@@ -510,10 +519,10 @@ FILE *mingw_fopen (const char *filename, const char *otype)
510519{
511520 int hide = needs_hiding (filename );
512521 FILE * file ;
513- wchar_t wfilename [MAX_PATH ], wotype [4 ];
522+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
514523 if (filename && !strcmp (filename , "/dev/null" ))
515524 filename = "nul" ;
516- if (xutftowcs_path (wfilename , filename ) < 0 ||
525+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
517526 xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
518527 return NULL ;
519528 if (hide && !access (filename , F_OK ) && set_hidden_flag (wfilename , 0 )) {
@@ -532,10 +541,10 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
532541{
533542 int hide = needs_hiding (filename );
534543 FILE * file ;
535- wchar_t wfilename [MAX_PATH ], wotype [4 ];
544+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
536545 if (filename && !strcmp (filename , "/dev/null" ))
537546 filename = "nul" ;
538- if (xutftowcs_path (wfilename , filename ) < 0 ||
547+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
539548 xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
540549 return NULL ;
541550 if (hide && !access (filename , F_OK ) && set_hidden_flag (wfilename , 0 )) {
@@ -589,25 +598,31 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
589598
590599int mingw_access (const char * filename , int mode )
591600{
592- wchar_t wfilename [MAX_PATH ];
593- if (xutftowcs_path (wfilename , filename ) < 0 )
601+ wchar_t wfilename [MAX_LONG_PATH ];
602+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
594603 return -1 ;
595604 /* X_OK is not supported by the MSVCRT version */
596605 return _waccess (wfilename , mode & ~X_OK );
597606}
598607
608+ /* cached length of current directory for handle_long_path */
609+ static int current_directory_len = 0 ;
610+
599611int mingw_chdir (const char * dirname )
600612{
601- wchar_t wdirname [MAX_PATH ];
602- if (xutftowcs_path (wdirname , dirname ) < 0 )
613+ int result ;
614+ wchar_t wdirname [MAX_LONG_PATH ];
615+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
603616 return -1 ;
604- return _wchdir (wdirname );
617+ result = _wchdir (wdirname );
618+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
619+ return result ;
605620}
606621
607622int mingw_chmod (const char * filename , int mode )
608623{
609- wchar_t wfilename [MAX_PATH ];
610- if (xutftowcs_path (wfilename , filename ) < 0 )
624+ wchar_t wfilename [MAX_LONG_PATH ];
625+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
611626 return -1 ;
612627 return _wchmod (wfilename , mode );
613628}
@@ -655,8 +670,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
655670static int do_lstat (int follow , const char * file_name , struct stat * buf )
656671{
657672 WIN32_FILE_ATTRIBUTE_DATA fdata ;
658- wchar_t wfilename [MAX_PATH ];
659- if (xutftowcs_path (wfilename , file_name ) < 0 )
673+ wchar_t wfilename [MAX_LONG_PATH ];
674+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
660675 return -1 ;
661676
662677 if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -727,7 +742,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
727742static int do_stat_internal (int follow , const char * file_name , struct stat * buf )
728743{
729744 int namelen ;
730- char alt_name [PATH_MAX ];
745+ char alt_name [MAX_LONG_PATH ];
731746
732747 if (!do_lstat (follow , file_name , buf ))
733748 return 0 ;
@@ -743,7 +758,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
743758 return -1 ;
744759 while (namelen && file_name [namelen - 1 ] == '/' )
745760 -- namelen ;
746- if (!namelen || namelen >= PATH_MAX )
761+ if (!namelen || namelen >= MAX_LONG_PATH )
747762 return -1 ;
748763
749764 memcpy (alt_name , file_name , namelen );
@@ -827,8 +842,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
827842 FILETIME mft , aft ;
828843 int fh , rc ;
829844 DWORD attrs ;
830- wchar_t wfilename [MAX_PATH ];
831- if (xutftowcs_path (wfilename , file_name ) < 0 )
845+ wchar_t wfilename [MAX_LONG_PATH ];
846+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
832847 return -1 ;
833848
834849 /* must have write permission */
@@ -887,11 +902,20 @@ unsigned int sleep (unsigned int seconds)
887902char * mingw_mktemp (char * template )
888903{
889904 wchar_t wtemplate [MAX_PATH ];
905+ int offset = 0 ;
906+
907+ /* we need to return the path, thus no long paths here! */
890908 if (xutftowcs_path (wtemplate , template ) < 0 )
891909 return NULL ;
910+
911+ if (is_dir_sep (template [0 ]) && !is_dir_sep (template [1 ]) &&
912+ iswalpha (wtemplate [0 ]) && wtemplate [1 ] == L':' ) {
913+ /* We have an absolute path missing the drive prefix */
914+ offset = 2 ;
915+ }
892916 if (!_wmktemp (wtemplate ))
893917 return NULL ;
894- if (xwcstoutf (template , wtemplate , strlen (template ) + 1 ) < 0 )
918+ if (xwcstoutf (template , wtemplate + offset , strlen (template ) + 1 ) < 0 )
895919 return NULL ;
896920 return template ;
897921}
@@ -1321,6 +1345,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
13211345 si .hStdOutput = winansi_get_osfhandle (fhout );
13221346 si .hStdError = winansi_get_osfhandle (fherr );
13231347
1348+ /* executables and the current directory don't support long paths */
13241349 if (xutftowcs_path (wcmd , cmd ) < 0 )
13251350 return -1 ;
13261351 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -1888,8 +1913,9 @@ int mingw_rename(const char *pold, const char *pnew)
18881913{
18891914 DWORD attrs , gle ;
18901915 int tries = 0 ;
1891- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
1892- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
1916+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
1917+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
1918+ xutftowcs_long_path (wpnew , pnew ) < 0 )
18931919 return -1 ;
18941920
18951921 /*
@@ -2153,24 +2179,12 @@ int mingw_raise(int sig)
21532179
21542180int link (const char * oldpath , const char * newpath )
21552181{
2156- typedef BOOL (WINAPI * T )(LPCWSTR , LPCWSTR , LPSECURITY_ATTRIBUTES );
2157- static T create_hard_link = NULL ;
2158- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2159- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2160- xutftowcs_path (wnewpath , newpath ) < 0 )
2182+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2183+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2184+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
21612185 return -1 ;
21622186
2163- if (!create_hard_link ) {
2164- create_hard_link = (T ) GetProcAddress (
2165- GetModuleHandle ("kernel32.dll" ), "CreateHardLinkW" );
2166- if (!create_hard_link )
2167- create_hard_link = (T )- 1 ;
2168- }
2169- if (create_hard_link == (T )- 1 ) {
2170- errno = ENOSYS ;
2171- return -1 ;
2172- }
2173- if (!create_hard_link (wnewpath , woldpath , NULL )) {
2187+ if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
21742188 errno = err_win_to_posix (GetLastError ());
21752189 return -1 ;
21762190 }
@@ -2370,6 +2384,68 @@ static void setup_windows_environment(void)
23702384 setenv ("TERM" , "cygwin" , 1 );
23712385}
23722386
2387+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
2388+ {
2389+ int result ;
2390+ wchar_t buf [MAX_LONG_PATH ];
2391+
2392+ /*
2393+ * we don't need special handling if path is relative to the current
2394+ * directory, and current directory + path don't exceed the desired
2395+ * max_path limit. This should cover > 99 % of cases with minimal
2396+ * performance impact (git almost always uses relative paths).
2397+ */
2398+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
2399+ (current_directory_len + len < max_path ))
2400+ return len ;
2401+
2402+ /*
2403+ * handle everything else:
2404+ * - absolute paths: "C:\dir\file"
2405+ * - absolute UNC paths: "\\server\share\dir\file"
2406+ * - absolute paths on current drive: "\dir\file"
2407+ * - relative paths on other drive: "X:file"
2408+ * - prefixed paths: "\\?\...", "\\.\..."
2409+ */
2410+
2411+ /* convert to absolute path using GetFullPathNameW */
2412+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
2413+ if (!result ) {
2414+ errno = err_win_to_posix (GetLastError ());
2415+ return -1 ;
2416+ }
2417+
2418+ /*
2419+ * return absolute path if it fits within max_path (even if
2420+ * "cwd + path" doesn't due to '..' components)
2421+ */
2422+ if (result < max_path ) {
2423+ wcscpy (path , buf );
2424+ return result ;
2425+ }
2426+
2427+ /* error out if we shouldn't expand the path or buf is too small */
2428+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
2429+ errno = ENAMETOOLONG ;
2430+ return -1 ;
2431+ }
2432+
2433+ /* prefix full path with "\\?\" or "\\?\UNC\" */
2434+ if (buf [0 ] == '\\' ) {
2435+ /* ...unless already prefixed */
2436+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
2437+ return len ;
2438+
2439+ wcscpy (path , L"\\\\?\\UNC\\" );
2440+ wcscpy (path + 8 , buf + 2 );
2441+ return result + 6 ;
2442+ } else {
2443+ wcscpy (path , L"\\\\?\\" );
2444+ wcscpy (path + 4 , buf );
2445+ return result + 4 ;
2446+ }
2447+ }
2448+
23732449/*
23742450 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
23752451 * mingw startup code, see init.c in mingw runtime).
@@ -2534,6 +2610,9 @@ int wmain(int argc, const wchar_t **wargv)
25342610 /* initialize Unicode console */
25352611 winansi_init ();
25362612
2613+ /* init length of current directory for handle_long_path */
2614+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
2615+
25372616 /* invoke the real main() using our utf8 version of argv. */
25382617 exit_status = main (argc , argv );
25392618
0 commit comments