@@ -235,6 +235,7 @@ static int core_restrict_inherited_handles = -1;
235235static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY ;
236236static char * unset_environment_variables ;
237237int core_fscache ;
238+ int core_long_paths ;
238239
239240int mingw_core_config (const char * var , const char * value , void * cb )
240241{
@@ -251,6 +252,11 @@ int mingw_core_config(const char *var, const char *value, void *cb)
251252 return 0 ;
252253 }
253254
255+ if (!strcmp (var , "core.longpaths" )) {
256+ core_long_paths = git_config_bool (var , value );
257+ return 0 ;
258+ }
259+
254260 if (!strcmp (var , "core.unsetenvvars" )) {
255261 free (unset_environment_variables );
256262 unset_environment_variables = xstrdup (value );
@@ -297,8 +303,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
297303int mingw_unlink (const char * pathname )
298304{
299305 int ret , tries = 0 ;
300- wchar_t wpathname [MAX_PATH ];
301- if (xutftowcs_path (wpathname , pathname ) < 0 )
306+ wchar_t wpathname [MAX_LONG_PATH ];
307+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
302308 return -1 ;
303309
304310 if (DeleteFileW (wpathname ))
@@ -330,7 +336,7 @@ static int is_dir_empty(const wchar_t *wpath)
330336{
331337 WIN32_FIND_DATAW findbuf ;
332338 HANDLE handle ;
333- wchar_t wbuf [MAX_PATH + 2 ];
339+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
334340 wcscpy (wbuf , wpath );
335341 wcscat (wbuf , L"\\*" );
336342 handle = FindFirstFileW (wbuf , & findbuf );
@@ -351,7 +357,7 @@ static int is_dir_empty(const wchar_t *wpath)
351357int mingw_rmdir (const char * pathname )
352358{
353359 int ret , tries = 0 ;
354- wchar_t wpathname [MAX_PATH ];
360+ wchar_t wpathname [MAX_LONG_PATH ];
355361 struct stat st ;
356362
357363 /*
@@ -373,7 +379,7 @@ int mingw_rmdir(const char *pathname)
373379 return -1 ;
374380 }
375381
376- if (xutftowcs_path (wpathname , pathname ) < 0 )
382+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
377383 return -1 ;
378384
379385 while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -452,15 +458,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
452458int mingw_mkdir (const char * path , int mode )
453459{
454460 int ret ;
455- wchar_t wpath [MAX_PATH ];
461+ wchar_t wpath [MAX_LONG_PATH ];
456462
457463 if (!is_valid_win32_path (path , 0 )) {
458464 errno = EINVAL ;
459465 return -1 ;
460466 }
461467
462- if (xutftowcs_path (wpath , path ) < 0 )
468+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
469+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
470+ core_long_paths ) < 0 )
463471 return -1 ;
472+
464473 ret = _wmkdir (wpath );
465474 if (!ret && needs_hiding (path ))
466475 return set_hidden_flag (wpath , 1 );
@@ -546,7 +555,7 @@ int mingw_open (const char *filename, int oflags, ...)
546555 va_list args ;
547556 unsigned mode ;
548557 int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
549- wchar_t wfilename [MAX_PATH ];
558+ wchar_t wfilename [MAX_LONG_PATH ];
550559 open_fn_t open_fn ;
551560
552561 va_start (args , oflags );
@@ -565,7 +574,7 @@ int mingw_open (const char *filename, int oflags, ...)
565574
566575 if (filename && !strcmp (filename , "/dev/null" ))
567576 wcscpy (wfilename , L"nul" );
568- else if (xutftowcs_path (wfilename , filename ) < 0 )
577+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
569578 return -1 ;
570579
571580 fd = open_fn (wfilename , oflags , mode );
@@ -623,14 +632,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
623632{
624633 int hide = needs_hiding (filename );
625634 FILE * file ;
626- wchar_t wfilename [MAX_PATH ], wotype [4 ];
635+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
627636 if (filename && !strcmp (filename , "/dev/null" ))
628637 wcscpy (wfilename , L"nul" );
629638 else if (!is_valid_win32_path (filename , 1 )) {
630639 int create = otype && strchr (otype , 'w' );
631640 errno = create ? EINVAL : ENOENT ;
632641 return NULL ;
633- } else if (xutftowcs_path (wfilename , filename ) < 0 )
642+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
634643 return NULL ;
635644
636645 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -652,14 +661,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
652661{
653662 int hide = needs_hiding (filename );
654663 FILE * file ;
655- wchar_t wfilename [MAX_PATH ], wotype [4 ];
664+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
656665 if (filename && !strcmp (filename , "/dev/null" ))
657666 wcscpy (wfilename , L"nul" );
658667 else if (!is_valid_win32_path (filename , 1 )) {
659668 int create = otype && strchr (otype , 'w' );
660669 errno = create ? EINVAL : ENOENT ;
661670 return NULL ;
662- } else if (xutftowcs_path (wfilename , filename ) < 0 )
671+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
663672 return NULL ;
664673
665674 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -716,27 +725,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
716725
717726int mingw_access (const char * filename , int mode )
718727{
719- wchar_t wfilename [MAX_PATH ];
728+ wchar_t wfilename [MAX_LONG_PATH ];
720729 if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
721730 return 0 ;
722- if (xutftowcs_path (wfilename , filename ) < 0 )
731+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
723732 return -1 ;
724733 /* X_OK is not supported by the MSVCRT version */
725734 return _waccess (wfilename , mode & ~X_OK );
726735}
727736
737+ /* cached length of current directory for handle_long_path */
738+ static int current_directory_len = 0 ;
739+
728740int mingw_chdir (const char * dirname )
729741{
730- wchar_t wdirname [MAX_PATH ];
731- if (xutftowcs_path (wdirname , dirname ) < 0 )
742+ int result ;
743+ wchar_t wdirname [MAX_LONG_PATH ];
744+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
732745 return -1 ;
733- return _wchdir (wdirname );
746+ result = _wchdir (wdirname );
747+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
748+ return result ;
734749}
735750
736751int mingw_chmod (const char * filename , int mode )
737752{
738- wchar_t wfilename [MAX_PATH ];
739- if (xutftowcs_path (wfilename , filename ) < 0 )
753+ wchar_t wfilename [MAX_LONG_PATH ];
754+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
740755 return -1 ;
741756 return _wchmod (wfilename , mode );
742757}
@@ -784,8 +799,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
784799static int do_lstat (int follow , const char * file_name , struct stat * buf )
785800{
786801 WIN32_FILE_ATTRIBUTE_DATA fdata ;
787- wchar_t wfilename [MAX_PATH ];
788- if (xutftowcs_path (wfilename , file_name ) < 0 )
802+ wchar_t wfilename [MAX_LONG_PATH ];
803+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
789804 return -1 ;
790805
791806 if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -956,10 +971,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
956971 FILETIME mft , aft ;
957972 int rc ;
958973 DWORD attrs ;
959- wchar_t wfilename [MAX_PATH ];
974+ wchar_t wfilename [MAX_LONG_PATH ];
960975 HANDLE osfilehandle ;
961976
962- if (xutftowcs_path (wfilename , file_name ) < 0 )
977+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
963978 return -1 ;
964979
965980 /* must have write permission */
@@ -1042,6 +1057,7 @@ char *mingw_mktemp(char *template)
10421057 wchar_t wtemplate [MAX_PATH ];
10431058 int offset = 0 ;
10441059
1060+ /* we need to return the path, thus no long paths here! */
10451061 if (xutftowcs_path (wtemplate , template ) < 0 )
10461062 return NULL ;
10471063
@@ -1679,6 +1695,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
16791695
16801696 if (* argv && !strcmp (cmd , * argv ))
16811697 wcmd [0 ] = L'\0' ;
1698+ /*
1699+ * Paths to executables and to the current directory do not support
1700+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1701+ */
16821702 else if (xutftowcs_path (wcmd , cmd ) < 0 )
16831703 return -1 ;
16841704 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2330,8 +2350,9 @@ int mingw_rename(const char *pold, const char *pnew)
23302350{
23312351 DWORD attrs , gle ;
23322352 int tries = 0 ;
2333- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2334- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2353+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2354+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2355+ xutftowcs_long_path (wpnew , pnew ) < 0 )
23352356 return -1 ;
23362357
23372358 /*
@@ -2645,9 +2666,9 @@ int mingw_raise(int sig)
26452666
26462667int link (const char * oldpath , const char * newpath )
26472668{
2648- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2649- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2650- xutftowcs_path (wnewpath , newpath ) < 0 )
2669+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2670+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2671+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
26512672 return -1 ;
26522673
26532674 if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2715,8 +2736,8 @@ int mingw_is_mount_point(struct strbuf *path)
27152736{
27162737 WIN32_FIND_DATAW findbuf = { 0 };
27172738 HANDLE handle ;
2718- wchar_t wfilename [MAX_PATH ];
2719- int wlen = xutftowcs_path (wfilename , path -> buf );
2739+ wchar_t wfilename [MAX_LONG_PATH ];
2740+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
27202741 if (wlen < 0 )
27212742 die (_ ("could not get long path for '%s'" ), path -> buf );
27222743
@@ -2861,9 +2882,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
28612882
28622883static int is_system32_path (const char * path )
28632884{
2864- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
2885+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
28652886
2866- if (xutftowcs_path (wpath , path ) < 0 ||
2887+ if (xutftowcs_long_path (wpath , path ) < 0 ||
28672888 !GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
28682889 _wcsicmp (system32 , wpath ))
28692890 return 0 ;
@@ -3205,6 +3226,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
32053226 }
32063227}
32073228
3229+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3230+ {
3231+ int result ;
3232+ wchar_t buf [MAX_LONG_PATH ];
3233+
3234+ /*
3235+ * we don't need special handling if path is relative to the current
3236+ * directory, and current directory + path don't exceed the desired
3237+ * max_path limit. This should cover > 99 % of cases with minimal
3238+ * performance impact (git almost always uses relative paths).
3239+ */
3240+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3241+ (current_directory_len + len < max_path ))
3242+ return len ;
3243+
3244+ /*
3245+ * handle everything else:
3246+ * - absolute paths: "C:\dir\file"
3247+ * - absolute UNC paths: "\\server\share\dir\file"
3248+ * - absolute paths on current drive: "\dir\file"
3249+ * - relative paths on other drive: "X:file"
3250+ * - prefixed paths: "\\?\...", "\\.\..."
3251+ */
3252+
3253+ /* convert to absolute path using GetFullPathNameW */
3254+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3255+ if (!result ) {
3256+ errno = err_win_to_posix (GetLastError ());
3257+ return -1 ;
3258+ }
3259+
3260+ /*
3261+ * return absolute path if it fits within max_path (even if
3262+ * "cwd + path" doesn't due to '..' components)
3263+ */
3264+ if (result < max_path ) {
3265+ wcscpy (path , buf );
3266+ return result ;
3267+ }
3268+
3269+ /* error out if we shouldn't expand the path or buf is too small */
3270+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3271+ errno = ENAMETOOLONG ;
3272+ return -1 ;
3273+ }
3274+
3275+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3276+ if (buf [0 ] == '\\' ) {
3277+ /* ...unless already prefixed */
3278+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3279+ return len ;
3280+
3281+ wcscpy (path , L"\\\\?\\UNC\\" );
3282+ wcscpy (path + 8 , buf + 2 );
3283+ return result + 6 ;
3284+ } else {
3285+ wcscpy (path , L"\\\\?\\" );
3286+ wcscpy (path + 4 , buf );
3287+ return result + 4 ;
3288+ }
3289+ }
3290+
32083291#if !defined(_MSC_VER )
32093292/*
32103293 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3366,6 +3449,9 @@ int wmain(int argc, const wchar_t **wargv)
33663449 /* initialize Unicode console */
33673450 winansi_init ();
33683451
3452+ /* init length of current directory for handle_long_path */
3453+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3454+
33693455 /* invoke the real main() using our utf8 version of argv. */
33703456 exit_status = main (argc , argv );
33713457
0 commit comments