@@ -248,6 +248,27 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
248248static char * unset_environment_variables ;
249249int core_fscache ;
250250
251+ int are_long_paths_enabled (void )
252+ {
253+ /* default to `false` during initialization */
254+ static const int fallback = 0 ;
255+
256+ static int enabled = -1 ;
257+
258+ if (enabled < 0 ) {
259+ /* avoid infinite recursion */
260+ if (!the_repository )
261+ return fallback ;
262+
263+ if (the_repository -> config &&
264+ the_repository -> config -> hash_initialized &&
265+ git_config_get_bool ("core.longpaths" , & enabled ) < 0 )
266+ enabled = 0 ;
267+ }
268+
269+ return enabled < 0 ? fallback : enabled ;
270+ }
271+
251272int mingw_core_config (const char * var , const char * value ,
252273 const struct config_context * ctx UNUSED ,
253274 void * cb UNUSED )
@@ -313,8 +334,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
313334int mingw_unlink (const char * pathname )
314335{
315336 int ret , tries = 0 ;
316- wchar_t wpathname [MAX_PATH ];
317- if (xutftowcs_path (wpathname , pathname ) < 0 )
337+ wchar_t wpathname [MAX_LONG_PATH ];
338+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
318339 return -1 ;
319340
320341 if (DeleteFileW (wpathname ))
@@ -346,7 +367,7 @@ static int is_dir_empty(const wchar_t *wpath)
346367{
347368 WIN32_FIND_DATAW findbuf ;
348369 HANDLE handle ;
349- wchar_t wbuf [MAX_PATH + 2 ];
370+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
350371 wcscpy (wbuf , wpath );
351372 wcscat (wbuf , L"\\*" );
352373 handle = FindFirstFileW (wbuf , & findbuf );
@@ -367,7 +388,7 @@ static int is_dir_empty(const wchar_t *wpath)
367388int mingw_rmdir (const char * pathname )
368389{
369390 int ret , tries = 0 ;
370- wchar_t wpathname [MAX_PATH ];
391+ wchar_t wpathname [MAX_LONG_PATH ];
371392 struct stat st ;
372393
373394 /*
@@ -389,7 +410,7 @@ int mingw_rmdir(const char *pathname)
389410 return -1 ;
390411 }
391412
392- if (xutftowcs_path (wpathname , pathname ) < 0 )
413+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
393414 return -1 ;
394415
395416 while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -468,15 +489,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
468489int mingw_mkdir (const char * path , int mode UNUSED )
469490{
470491 int ret ;
471- wchar_t wpath [MAX_PATH ];
492+ wchar_t wpath [MAX_LONG_PATH ];
472493
473494 if (!is_valid_win32_path (path , 0 )) {
474495 errno = EINVAL ;
475496 return -1 ;
476497 }
477498
478- if (xutftowcs_path (wpath , path ) < 0 )
499+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
500+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
501+ are_long_paths_enabled ()) < 0 )
479502 return -1 ;
503+
480504 ret = _wmkdir (wpath );
481505 if (!ret && needs_hiding (path ))
482506 return set_hidden_flag (wpath , 1 );
@@ -563,7 +587,7 @@ int mingw_open (const char *filename, int oflags, ...)
563587 va_list args ;
564588 unsigned mode ;
565589 int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
566- wchar_t wfilename [MAX_PATH ];
590+ wchar_t wfilename [MAX_LONG_PATH ];
567591 open_fn_t open_fn ;
568592
569593 va_start (args , oflags );
@@ -591,7 +615,7 @@ int mingw_open (const char *filename, int oflags, ...)
591615
592616 if (filename && !strcmp (filename , "/dev/null" ))
593617 wcscpy (wfilename , L"nul" );
594- else if (xutftowcs_path (wfilename , filename ) < 0 )
618+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
595619 return -1 ;
596620
597621 fd = open_fn (wfilename , oflags , mode );
@@ -649,14 +673,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
649673{
650674 int hide = needs_hiding (filename );
651675 FILE * file ;
652- wchar_t wfilename [MAX_PATH ], wotype [4 ];
676+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
653677 if (filename && !strcmp (filename , "/dev/null" ))
654678 wcscpy (wfilename , L"nul" );
655679 else if (!is_valid_win32_path (filename , 1 )) {
656680 int create = otype && strchr (otype , 'w' );
657681 errno = create ? EINVAL : ENOENT ;
658682 return NULL ;
659- } else if (xutftowcs_path (wfilename , filename ) < 0 )
683+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
660684 return NULL ;
661685
662686 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -678,14 +702,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
678702{
679703 int hide = needs_hiding (filename );
680704 FILE * file ;
681- wchar_t wfilename [MAX_PATH ], wotype [4 ];
705+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
682706 if (filename && !strcmp (filename , "/dev/null" ))
683707 wcscpy (wfilename , L"nul" );
684708 else if (!is_valid_win32_path (filename , 1 )) {
685709 int create = otype && strchr (otype , 'w' );
686710 errno = create ? EINVAL : ENOENT ;
687711 return NULL ;
688- } else if (xutftowcs_path (wfilename , filename ) < 0 )
712+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
689713 return NULL ;
690714
691715 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -735,7 +759,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
735759 HANDLE h = (HANDLE ) _get_osfhandle (fd );
736760 if (GetFileType (h ) != FILE_TYPE_PIPE ) {
737761 if (orig == EINVAL ) {
738- wchar_t path [MAX_PATH ];
762+ wchar_t path [MAX_LONG_PATH ];
739763 DWORD ret = GetFinalPathNameByHandleW (h , path ,
740764 ARRAY_SIZE (path ), 0 );
741765 UINT drive_type = ret > 0 && ret < ARRAY_SIZE (path ) ?
@@ -772,27 +796,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
772796
773797int mingw_access (const char * filename , int mode )
774798{
775- wchar_t wfilename [MAX_PATH ];
799+ wchar_t wfilename [MAX_LONG_PATH ];
776800 if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
777801 return 0 ;
778- if (xutftowcs_path (wfilename , filename ) < 0 )
802+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
779803 return -1 ;
780804 /* X_OK is not supported by the MSVCRT version */
781805 return _waccess (wfilename , mode & ~X_OK );
782806}
783807
808+ /* cached length of current directory for handle_long_path */
809+ static int current_directory_len = 0 ;
810+
784811int mingw_chdir (const char * dirname )
785812{
786- wchar_t wdirname [MAX_PATH ];
787- if (xutftowcs_path (wdirname , dirname ) < 0 )
813+ int result ;
814+ wchar_t wdirname [MAX_LONG_PATH ];
815+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
788816 return -1 ;
789- return _wchdir (wdirname );
817+ result = _wchdir (wdirname );
818+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
819+ return result ;
790820}
791821
792822int mingw_chmod (const char * filename , int mode )
793823{
794- wchar_t wfilename [MAX_PATH ];
795- if (xutftowcs_path (wfilename , filename ) < 0 )
824+ wchar_t wfilename [MAX_LONG_PATH ];
825+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
796826 return -1 ;
797827 return _wchmod (wfilename , mode );
798828}
@@ -840,8 +870,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
840870static int do_lstat (int follow , const char * file_name , struct stat * buf )
841871{
842872 WIN32_FILE_ATTRIBUTE_DATA fdata ;
843- wchar_t wfilename [MAX_PATH ];
844- if (xutftowcs_path (wfilename , file_name ) < 0 )
873+ wchar_t wfilename [MAX_LONG_PATH ];
874+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
845875 return -1 ;
846876
847877 if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -1012,10 +1042,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
10121042 FILETIME mft , aft ;
10131043 int rc ;
10141044 DWORD attrs ;
1015- wchar_t wfilename [MAX_PATH ];
1045+ wchar_t wfilename [MAX_LONG_PATH ];
10161046 HANDLE osfilehandle ;
10171047
1018- if (xutftowcs_path (wfilename , file_name ) < 0 )
1048+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
10191049 return -1 ;
10201050
10211051 /* must have write permission */
@@ -1098,6 +1128,7 @@ char *mingw_mktemp(char *template)
10981128 wchar_t wtemplate [MAX_PATH ];
10991129 int offset = 0 ;
11001130
1131+ /* we need to return the path, thus no long paths here! */
11011132 if (xutftowcs_path (wtemplate , template ) < 0 )
11021133 return NULL ;
11031134
@@ -1749,6 +1780,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
17491780
17501781 if (* argv && !strcmp (cmd , * argv ))
17511782 wcmd [0 ] = L'\0' ;
1783+ /*
1784+ * Paths to executables and to the current directory do not support
1785+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1786+ */
17521787 else if (xutftowcs_path (wcmd , cmd ) < 0 )
17531788 return -1 ;
17541789 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2397,8 +2432,9 @@ int mingw_rename(const char *pold, const char *pnew)
23972432{
23982433 DWORD attrs , gle ;
23992434 int tries = 0 ;
2400- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2401- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2435+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2436+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2437+ xutftowcs_long_path (wpnew , pnew ) < 0 )
24022438 return -1 ;
24032439
24042440 /*
@@ -2716,9 +2752,9 @@ int mingw_raise(int sig)
27162752
27172753int link (const char * oldpath , const char * newpath )
27182754{
2719- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2720- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2721- xutftowcs_path (wnewpath , newpath ) < 0 )
2755+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2756+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2757+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
27222758 return -1 ;
27232759
27242760 if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2786,8 +2822,8 @@ int mingw_is_mount_point(struct strbuf *path)
27862822{
27872823 WIN32_FIND_DATAW findbuf = { 0 };
27882824 HANDLE handle ;
2789- wchar_t wfilename [MAX_PATH ];
2790- int wlen = xutftowcs_path (wfilename , path -> buf );
2825+ wchar_t wfilename [MAX_LONG_PATH ];
2826+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
27912827 if (wlen < 0 )
27922828 die (_ ("could not get long path for '%s'" ), path -> buf );
27932829
@@ -2932,9 +2968,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
29322968
29332969static int is_system32_path (const char * path )
29342970{
2935- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
2971+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
29362972
2937- if (xutftowcs_path (wpath , path ) < 0 ||
2973+ if (xutftowcs_long_path (wpath , path ) < 0 ||
29382974 !GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
29392975 _wcsicmp (system32 , wpath ))
29402976 return 0 ;
@@ -3346,6 +3382,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
33463382 }
33473383}
33483384
3385+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3386+ {
3387+ int result ;
3388+ wchar_t buf [MAX_LONG_PATH ];
3389+
3390+ /*
3391+ * we don't need special handling if path is relative to the current
3392+ * directory, and current directory + path don't exceed the desired
3393+ * max_path limit. This should cover > 99 % of cases with minimal
3394+ * performance impact (git almost always uses relative paths).
3395+ */
3396+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3397+ (current_directory_len + len < max_path ))
3398+ return len ;
3399+
3400+ /*
3401+ * handle everything else:
3402+ * - absolute paths: "C:\dir\file"
3403+ * - absolute UNC paths: "\\server\share\dir\file"
3404+ * - absolute paths on current drive: "\dir\file"
3405+ * - relative paths on other drive: "X:file"
3406+ * - prefixed paths: "\\?\...", "\\.\..."
3407+ */
3408+
3409+ /* convert to absolute path using GetFullPathNameW */
3410+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3411+ if (!result ) {
3412+ errno = err_win_to_posix (GetLastError ());
3413+ return -1 ;
3414+ }
3415+
3416+ /*
3417+ * return absolute path if it fits within max_path (even if
3418+ * "cwd + path" doesn't due to '..' components)
3419+ */
3420+ if (result < max_path ) {
3421+ wcscpy (path , buf );
3422+ return result ;
3423+ }
3424+
3425+ /* error out if we shouldn't expand the path or buf is too small */
3426+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3427+ errno = ENAMETOOLONG ;
3428+ return -1 ;
3429+ }
3430+
3431+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3432+ if (buf [0 ] == '\\' ) {
3433+ /* ...unless already prefixed */
3434+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3435+ return len ;
3436+
3437+ wcscpy (path , L"\\\\?\\UNC\\" );
3438+ wcscpy (path + 8 , buf + 2 );
3439+ return result + 6 ;
3440+ } else {
3441+ wcscpy (path , L"\\\\?\\" );
3442+ wcscpy (path + 4 , buf );
3443+ return result + 4 ;
3444+ }
3445+ }
3446+
33493447#if !defined(_MSC_VER )
33503448/*
33513449 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3507,6 +3605,9 @@ int wmain(int argc, const wchar_t **wargv)
35073605 /* initialize Unicode console */
35083606 winansi_init ();
35093607
3608+ /* init length of current directory for handle_long_path */
3609+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3610+
35103611 /* invoke the real main() using our utf8 version of argv. */
35113612 exit_status = main (argc , argv );
35123613
0 commit comments