@@ -204,8 +204,8 @@ static int ask_yes_no_if_possible(const char *format, ...)
204204int mingw_unlink (const char * pathname )
205205{
206206 int ret , tries = 0 ;
207- wchar_t wpathname [MAX_PATH ];
208- if (xutftowcs_path (wpathname , pathname ) < 0 )
207+ wchar_t wpathname [MAX_LONG_PATH ];
208+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
209209 return -1 ;
210210
211211 /* read-only files cannot be removed */
@@ -234,7 +234,7 @@ static int is_dir_empty(const wchar_t *wpath)
234234{
235235 WIN32_FIND_DATAW findbuf ;
236236 HANDLE handle ;
237- wchar_t wbuf [MAX_PATH + 2 ];
237+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
238238 wcscpy (wbuf , wpath );
239239 wcscat (wbuf , L"\\*" );
240240 handle = FindFirstFileW (wbuf , & findbuf );
@@ -255,8 +255,8 @@ static int is_dir_empty(const wchar_t *wpath)
255255int mingw_rmdir (const char * pathname )
256256{
257257 int ret , tries = 0 ;
258- wchar_t wpathname [MAX_PATH ];
259- if (xutftowcs_path (wpathname , pathname ) < 0 )
258+ wchar_t wpathname [MAX_LONG_PATH ];
259+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
260260 return -1 ;
261261
262262 while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -296,9 +296,9 @@ static int make_hidden(const wchar_t *path)
296296
297297void mingw_mark_as_git_dir (const char * dir )
298298{
299- wchar_t wdir [MAX_PATH ];
299+ wchar_t wdir [MAX_LONG_PATH ];
300300 if (hide_dotfiles != HIDE_DOTFILES_FALSE && !is_bare_repository ())
301- if (xutftowcs_path (wdir , dir ) < 0 || make_hidden (wdir ))
301+ if (xutftowcs_long_path (wdir , dir ) < 0 || make_hidden (wdir ))
302302 warning ("Failed to make '%s' hidden" , dir );
303303 git_config_set ("core.hideDotFiles" ,
304304 hide_dotfiles == HIDE_DOTFILES_FALSE ? "false" :
@@ -309,9 +309,12 @@ void mingw_mark_as_git_dir(const char *dir)
309309int mingw_mkdir (const char * path , int mode )
310310{
311311 int ret ;
312- wchar_t wpath [MAX_PATH ];
313- if (xutftowcs_path (wpath , path ) < 0 )
312+ wchar_t wpath [MAX_LONG_PATH ];
313+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
314+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
315+ core_long_paths ) < 0 )
314316 return -1 ;
317+
315318 ret = _wmkdir (wpath );
316319 if (!ret && hide_dotfiles == HIDE_DOTFILES_TRUE ) {
317320 /*
@@ -331,7 +334,7 @@ int mingw_open (const char *filename, int oflags, ...)
331334 va_list args ;
332335 unsigned mode ;
333336 int fd ;
334- wchar_t wfilename [MAX_PATH ];
337+ wchar_t wfilename [MAX_LONG_PATH ];
335338
336339 va_start (args , oflags );
337340 mode = va_arg (args , int );
@@ -340,7 +343,7 @@ int mingw_open (const char *filename, int oflags, ...)
340343 if (filename && !strcmp (filename , "/dev/null" ))
341344 filename = "nul" ;
342345
343- if (xutftowcs_path (wfilename , filename ) < 0 )
346+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
344347 return -1 ;
345348 fd = _wopen (wfilename , oflags , mode );
346349
@@ -393,13 +396,13 @@ FILE *mingw_fopen (const char *filename, const char *otype)
393396{
394397 int hide = 0 ;
395398 FILE * file ;
396- wchar_t wfilename [MAX_PATH ], wotype [4 ];
399+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
397400 if (hide_dotfiles == HIDE_DOTFILES_TRUE &&
398401 basename ((char * )filename )[0 ] == '.' )
399402 hide = access (filename , F_OK );
400403 if (filename && !strcmp (filename , "/dev/null" ))
401404 filename = "nul" ;
402- if (xutftowcs_path (wfilename , filename ) < 0 ||
405+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
403406 xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
404407 return NULL ;
405408 file = _wfopen (wfilename , wotype );
@@ -412,13 +415,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
412415{
413416 int hide = 0 ;
414417 FILE * file ;
415- wchar_t wfilename [MAX_PATH ], wotype [4 ];
418+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
416419 if (hide_dotfiles == HIDE_DOTFILES_TRUE &&
417420 basename ((char * )filename )[0 ] == '.' )
418421 hide = access (filename , F_OK );
419422 if (filename && !strcmp (filename , "/dev/null" ))
420423 filename = "nul" ;
421- if (xutftowcs_path (wfilename , filename ) < 0 ||
424+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
422425 xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
423426 return NULL ;
424427 file = _wfreopen (wfilename , wotype , stream );
@@ -451,25 +454,32 @@ int mingw_fflush(FILE *stream)
451454
452455int mingw_access (const char * filename , int mode )
453456{
454- wchar_t wfilename [MAX_PATH ];
455- if (xutftowcs_path (wfilename , filename ) < 0 )
457+ wchar_t wfilename [MAX_LONG_PATH ];
458+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
456459 return -1 ;
457460 /* X_OK is not supported by the MSVCRT version */
458461 return _waccess (wfilename , mode & ~X_OK );
459462}
460463
464+ /* cached length of current directory for handle_long_path */
465+ static int current_directory_len = 0 ;
466+
461467int mingw_chdir (const char * dirname )
462468{
469+ int result ;
463470 wchar_t wdirname [MAX_PATH ];
471+ /* SetCurrentDirectoryW doesn't support long paths */
464472 if (xutftowcs_path (wdirname , dirname ) < 0 )
465473 return -1 ;
466- return _wchdir (wdirname );
474+ result = _wchdir (wdirname );
475+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
476+ return result ;
467477}
468478
469479int mingw_chmod (const char * filename , int mode )
470480{
471- wchar_t wfilename [MAX_PATH ];
472- if (xutftowcs_path (wfilename , filename ) < 0 )
481+ wchar_t wfilename [MAX_LONG_PATH ];
482+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
473483 return -1 ;
474484 return _wchmod (wfilename , mode );
475485}
@@ -484,8 +494,8 @@ int mingw_chmod(const char *filename, int mode)
484494static int do_lstat (int follow , const char * file_name , struct stat * buf )
485495{
486496 WIN32_FILE_ATTRIBUTE_DATA fdata ;
487- wchar_t wfilename [MAX_PATH ];
488- if (xutftowcs_path (wfilename , file_name ) < 0 )
497+ wchar_t wfilename [MAX_LONG_PATH ];
498+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
489499 return -1 ;
490500
491501 if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -550,7 +560,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
550560static int do_stat_internal (int follow , const char * file_name , struct stat * buf )
551561{
552562 int namelen ;
553- char alt_name [PATH_MAX ];
563+ char alt_name [MAX_LONG_PATH ];
554564
555565 if (!do_lstat (follow , file_name , buf ))
556566 return 0 ;
@@ -566,7 +576,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
566576 return -1 ;
567577 while (namelen && file_name [namelen - 1 ] == '/' )
568578 -- namelen ;
569- if (!namelen || namelen >= PATH_MAX )
579+ if (!namelen || namelen >= MAX_LONG_PATH )
570580 return -1 ;
571581
572582 memcpy (alt_name , file_name , namelen );
@@ -628,8 +638,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
628638 FILETIME mft , aft ;
629639 int fh , rc ;
630640 DWORD attrs ;
631- wchar_t wfilename [MAX_PATH ];
632- if (xutftowcs_path (wfilename , file_name ) < 0 )
641+ wchar_t wfilename [MAX_LONG_PATH ];
642+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
633643 return -1 ;
634644
635645 /* must have write permission */
@@ -677,6 +687,7 @@ unsigned int sleep (unsigned int seconds)
677687char * mingw_mktemp (char * template )
678688{
679689 wchar_t wtemplate [MAX_PATH ];
690+ /* we need to return the path, thus no long paths here! */
680691 if (xutftowcs_path (wtemplate , template ) < 0 )
681692 return NULL ;
682693 if (!_wmktemp (wtemplate ))
@@ -1037,6 +1048,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
10371048 si .hStdOutput = winansi_get_osfhandle (fhout );
10381049 si .hStdError = winansi_get_osfhandle (fherr );
10391050
1051+ /* executables and the current directory don't support long paths */
10401052 if (xutftowcs_path (wcmd , cmd ) < 0 )
10411053 return -1 ;
10421054 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -1612,8 +1624,9 @@ int mingw_rename(const char *pold, const char *pnew)
16121624{
16131625 DWORD attrs , gle ;
16141626 int tries = 0 ;
1615- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
1616- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
1627+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
1628+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
1629+ xutftowcs_long_path (wpnew , pnew ) < 0 )
16171630 return -1 ;
16181631
16191632 /*
@@ -1889,9 +1902,9 @@ int link(const char *oldpath, const char *newpath)
18891902{
18901903 typedef BOOL (WINAPI * T )(LPCWSTR , LPCWSTR , LPSECURITY_ATTRIBUTES );
18911904 static T create_hard_link = NULL ;
1892- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
1893- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
1894- xutftowcs_path (wnewpath , newpath ) < 0 )
1905+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
1906+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
1907+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
18951908 return -1 ;
18961909
18971910 if (!create_hard_link ) {
@@ -2072,6 +2085,68 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
20722085 return -1 ;
20732086}
20742087
2088+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
2089+ {
2090+ int result ;
2091+ wchar_t buf [MAX_LONG_PATH ];
2092+
2093+ /*
2094+ * we don't need special handling if path is relative to the current
2095+ * directory, and current directory + path don't exceed the desired
2096+ * max_path limit. This should cover > 99 % of cases with minimal
2097+ * performance impact (git almost always uses relative paths).
2098+ */
2099+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
2100+ (current_directory_len + len < max_path ))
2101+ return len ;
2102+
2103+ /*
2104+ * handle everything else:
2105+ * - absolute paths: "C:\dir\file"
2106+ * - absolute UNC paths: "\\server\share\dir\file"
2107+ * - absolute paths on current drive: "\dir\file"
2108+ * - relative paths on other drive: "X:file"
2109+ * - prefixed paths: "\\?\...", "\\.\..."
2110+ */
2111+
2112+ /* convert to absolute path using GetFullPathNameW */
2113+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
2114+ if (!result ) {
2115+ errno = err_win_to_posix (GetLastError ());
2116+ return -1 ;
2117+ }
2118+
2119+ /*
2120+ * return absolute path if it fits within max_path (even if
2121+ * "cwd + path" doesn't due to '..' components)
2122+ */
2123+ if (result < max_path ) {
2124+ wcscpy (path , buf );
2125+ return result ;
2126+ }
2127+
2128+ /* error out if we shouldn't expand the path or buf is too small */
2129+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
2130+ errno = ENAMETOOLONG ;
2131+ return -1 ;
2132+ }
2133+
2134+ /* prefix full path with "\\?\" or "\\?\UNC\" */
2135+ if (buf [0 ] == '\\' ) {
2136+ /* ...unless already prefixed */
2137+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
2138+ return len ;
2139+
2140+ wcscpy (path , L"\\\\?\\UNC\\" );
2141+ wcscpy (path + 8 , buf + 2 );
2142+ return result + 6 ;
2143+ } else {
2144+ wcscpy (path , L"\\\\?\\" );
2145+ wcscpy (path + 4 , buf );
2146+ return result + 4 ;
2147+ }
2148+ }
2149+
20752150/*
20762151 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
20772152 * mingw startup code, see init.c in mingw runtime).
@@ -2175,4 +2250,7 @@ void mingw_startup()
21752250
21762251 /* initialize Unicode console */
21772252 winansi_init ();
2253+
2254+ /* init length of current directory for handle_long_path */
2255+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
21782256}
0 commit comments