@@ -234,6 +234,7 @@ static int core_restrict_inherited_handles = -1;
234234static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY ;
235235static char * unset_environment_variables ;
236236int core_fscache ;
237+ int core_long_paths ;
237238
238239int mingw_core_config (const char * var , const char * value , void * cb )
239240{
@@ -250,6 +251,11 @@ int mingw_core_config(const char *var, const char *value, void *cb)
250251 return 0 ;
251252 }
252253
254+ if (!strcmp (var , "core.longpaths" )) {
255+ core_long_paths = git_config_bool (var , value );
256+ return 0 ;
257+ }
258+
253259 if (!strcmp (var , "core.unsetenvvars" )) {
254260 free (unset_environment_variables );
255261 unset_environment_variables = xstrdup (value );
@@ -296,8 +302,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
296302int mingw_unlink (const char * pathname )
297303{
298304 int ret , tries = 0 ;
299- wchar_t wpathname [MAX_PATH ];
300- if (xutftowcs_path (wpathname , pathname ) < 0 )
305+ wchar_t wpathname [MAX_LONG_PATH ];
306+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
301307 return -1 ;
302308
303309 if (DeleteFileW (wpathname ))
@@ -329,7 +335,7 @@ static int is_dir_empty(const wchar_t *wpath)
329335{
330336 WIN32_FIND_DATAW findbuf ;
331337 HANDLE handle ;
332- wchar_t wbuf [MAX_PATH + 2 ];
338+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
333339 wcscpy (wbuf , wpath );
334340 wcscat (wbuf , L"\\*" );
335341 handle = FindFirstFileW (wbuf , & findbuf );
@@ -350,7 +356,7 @@ static int is_dir_empty(const wchar_t *wpath)
350356int mingw_rmdir (const char * pathname )
351357{
352358 int ret , tries = 0 ;
353- wchar_t wpathname [MAX_PATH ];
359+ wchar_t wpathname [MAX_LONG_PATH ];
354360 struct stat st ;
355361
356362 /*
@@ -372,7 +378,7 @@ int mingw_rmdir(const char *pathname)
372378 return -1 ;
373379 }
374380
375- if (xutftowcs_path (wpathname , pathname ) < 0 )
381+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
376382 return -1 ;
377383
378384 while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -451,15 +457,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
451457int mingw_mkdir (const char * path , int mode )
452458{
453459 int ret ;
454- wchar_t wpath [MAX_PATH ];
460+ wchar_t wpath [MAX_LONG_PATH ];
455461
456462 if (!is_valid_win32_path (path , 0 )) {
457463 errno = EINVAL ;
458464 return -1 ;
459465 }
460466
461- if (xutftowcs_path (wpath , path ) < 0 )
467+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
468+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
469+ core_long_paths ) < 0 )
462470 return -1 ;
471+
463472 ret = _wmkdir (wpath );
464473 if (!ret && needs_hiding (path ))
465474 return set_hidden_flag (wpath , 1 );
@@ -545,7 +554,7 @@ int mingw_open (const char *filename, int oflags, ...)
545554 va_list args ;
546555 unsigned mode ;
547556 int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
548- wchar_t wfilename [MAX_PATH ];
557+ wchar_t wfilename [MAX_LONG_PATH ];
549558 open_fn_t open_fn ;
550559
551560 va_start (args , oflags );
@@ -564,7 +573,7 @@ int mingw_open (const char *filename, int oflags, ...)
564573
565574 if (filename && !strcmp (filename , "/dev/null" ))
566575 wcscpy (wfilename , L"nul" );
567- else if (xutftowcs_path (wfilename , filename ) < 0 )
576+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
568577 return -1 ;
569578
570579 fd = open_fn (wfilename , oflags , mode );
@@ -622,14 +631,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
622631{
623632 int hide = needs_hiding (filename );
624633 FILE * file ;
625- wchar_t wfilename [MAX_PATH ], wotype [4 ];
634+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
626635 if (filename && !strcmp (filename , "/dev/null" ))
627636 wcscpy (wfilename , L"nul" );
628637 else if (!is_valid_win32_path (filename , 1 )) {
629638 int create = otype && strchr (otype , 'w' );
630639 errno = create ? EINVAL : ENOENT ;
631640 return NULL ;
632- } else if (xutftowcs_path (wfilename , filename ) < 0 )
641+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
633642 return NULL ;
634643
635644 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -651,14 +660,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
651660{
652661 int hide = needs_hiding (filename );
653662 FILE * file ;
654- wchar_t wfilename [MAX_PATH ], wotype [4 ];
663+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
655664 if (filename && !strcmp (filename , "/dev/null" ))
656665 wcscpy (wfilename , L"nul" );
657666 else if (!is_valid_win32_path (filename , 1 )) {
658667 int create = otype && strchr (otype , 'w' );
659668 errno = create ? EINVAL : ENOENT ;
660669 return NULL ;
661- } else if (xutftowcs_path (wfilename , filename ) < 0 )
670+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
662671 return NULL ;
663672
664673 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -715,27 +724,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
715724
716725int mingw_access (const char * filename , int mode )
717726{
718- wchar_t wfilename [MAX_PATH ];
727+ wchar_t wfilename [MAX_LONG_PATH ];
719728 if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
720729 return 0 ;
721- if (xutftowcs_path (wfilename , filename ) < 0 )
730+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
722731 return -1 ;
723732 /* X_OK is not supported by the MSVCRT version */
724733 return _waccess (wfilename , mode & ~X_OK );
725734}
726735
736+ /* cached length of current directory for handle_long_path */
737+ static int current_directory_len = 0 ;
738+
727739int mingw_chdir (const char * dirname )
728740{
729- wchar_t wdirname [MAX_PATH ];
730- if (xutftowcs_path (wdirname , dirname ) < 0 )
741+ int result ;
742+ wchar_t wdirname [MAX_LONG_PATH ];
743+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
731744 return -1 ;
732- return _wchdir (wdirname );
745+ result = _wchdir (wdirname );
746+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
747+ return result ;
733748}
734749
735750int mingw_chmod (const char * filename , int mode )
736751{
737- wchar_t wfilename [MAX_PATH ];
738- if (xutftowcs_path (wfilename , filename ) < 0 )
752+ wchar_t wfilename [MAX_LONG_PATH ];
753+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
739754 return -1 ;
740755 return _wchmod (wfilename , mode );
741756}
@@ -783,8 +798,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
783798static int do_lstat (int follow , const char * file_name , struct stat * buf )
784799{
785800 WIN32_FILE_ATTRIBUTE_DATA fdata ;
786- wchar_t wfilename [MAX_PATH ];
787- if (xutftowcs_path (wfilename , file_name ) < 0 )
801+ wchar_t wfilename [MAX_LONG_PATH ];
802+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
788803 return -1 ;
789804
790805 if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -955,10 +970,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
955970 FILETIME mft , aft ;
956971 int rc ;
957972 DWORD attrs ;
958- wchar_t wfilename [MAX_PATH ];
973+ wchar_t wfilename [MAX_LONG_PATH ];
959974 HANDLE osfilehandle ;
960975
961- if (xutftowcs_path (wfilename , file_name ) < 0 )
976+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
962977 return -1 ;
963978
964979 /* must have write permission */
@@ -1041,6 +1056,7 @@ char *mingw_mktemp(char *template)
10411056 wchar_t wtemplate [MAX_PATH ];
10421057 int offset = 0 ;
10431058
1059+ /* we need to return the path, thus no long paths here! */
10441060 if (xutftowcs_path (wtemplate , template ) < 0 )
10451061 return NULL ;
10461062
@@ -1678,6 +1694,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
16781694
16791695 if (* argv && !strcmp (cmd , * argv ))
16801696 wcmd [0 ] = L'\0' ;
1697+ /*
1698+ * Paths to executables and to the current directory do not support
1699+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1700+ */
16811701 else if (xutftowcs_path (wcmd , cmd ) < 0 )
16821702 return -1 ;
16831703 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2329,8 +2349,9 @@ int mingw_rename(const char *pold, const char *pnew)
23292349{
23302350 DWORD attrs , gle ;
23312351 int tries = 0 ;
2332- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2333- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2352+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2353+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2354+ xutftowcs_long_path (wpnew , pnew ) < 0 )
23342355 return -1 ;
23352356
23362357 /*
@@ -2644,9 +2665,9 @@ int mingw_raise(int sig)
26442665
26452666int link (const char * oldpath , const char * newpath )
26462667{
2647- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2648- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2649- xutftowcs_path (wnewpath , newpath ) < 0 )
2668+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2669+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2670+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
26502671 return -1 ;
26512672
26522673 if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2714,8 +2735,8 @@ int mingw_is_mount_point(struct strbuf *path)
27142735{
27152736 WIN32_FIND_DATAW findbuf = { 0 };
27162737 HANDLE handle ;
2717- wchar_t wfilename [MAX_PATH ];
2718- int wlen = xutftowcs_path (wfilename , path -> buf );
2738+ wchar_t wfilename [MAX_LONG_PATH ];
2739+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
27192740 if (wlen < 0 )
27202741 die (_ ("could not get long path for '%s'" ), path -> buf );
27212742
@@ -2860,9 +2881,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
28602881
28612882static int is_system32_path (const char * path )
28622883{
2863- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
2884+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
28642885
2865- if (xutftowcs_path (wpath , path ) < 0 ||
2886+ if (xutftowcs_long_path (wpath , path ) < 0 ||
28662887 !GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
28672888 _wcsicmp (system32 , wpath ))
28682889 return 0 ;
@@ -3165,6 +3186,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
31653186 }
31663187}
31673188
3189+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3190+ {
3191+ int result ;
3192+ wchar_t buf [MAX_LONG_PATH ];
3193+
3194+ /*
3195+ * we don't need special handling if path is relative to the current
3196+ * directory, and current directory + path don't exceed the desired
3197+ * max_path limit. This should cover > 99 % of cases with minimal
3198+ * performance impact (git almost always uses relative paths).
3199+ */
3200+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3201+ (current_directory_len + len < max_path ))
3202+ return len ;
3203+
3204+ /*
3205+ * handle everything else:
3206+ * - absolute paths: "C:\dir\file"
3207+ * - absolute UNC paths: "\\server\share\dir\file"
3208+ * - absolute paths on current drive: "\dir\file"
3209+ * - relative paths on other drive: "X:file"
3210+ * - prefixed paths: "\\?\...", "\\.\..."
3211+ */
3212+
3213+ /* convert to absolute path using GetFullPathNameW */
3214+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3215+ if (!result ) {
3216+ errno = err_win_to_posix (GetLastError ());
3217+ return -1 ;
3218+ }
3219+
3220+ /*
3221+ * return absolute path if it fits within max_path (even if
3222+ * "cwd + path" doesn't due to '..' components)
3223+ */
3224+ if (result < max_path ) {
3225+ wcscpy (path , buf );
3226+ return result ;
3227+ }
3228+
3229+ /* error out if we shouldn't expand the path or buf is too small */
3230+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3231+ errno = ENAMETOOLONG ;
3232+ return -1 ;
3233+ }
3234+
3235+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3236+ if (buf [0 ] == '\\' ) {
3237+ /* ...unless already prefixed */
3238+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3239+ return len ;
3240+
3241+ wcscpy (path , L"\\\\?\\UNC\\" );
3242+ wcscpy (path + 8 , buf + 2 );
3243+ return result + 6 ;
3244+ } else {
3245+ wcscpy (path , L"\\\\?\\" );
3246+ wcscpy (path + 4 , buf );
3247+ return result + 4 ;
3248+ }
3249+ }
3250+
31683251#if !defined(_MSC_VER )
31693252/*
31703253 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3326,6 +3409,9 @@ int wmain(int argc, const wchar_t **wargv)
33263409 /* initialize Unicode console */
33273410 winansi_init ();
33283411
3412+ /* init length of current directory for handle_long_path */
3413+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3414+
33293415 /* invoke the real main() using our utf8 version of argv. */
33303416 exit_status = main (argc , argv );
33313417
0 commit comments