@@ -247,6 +247,27 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
247247static char * unset_environment_variables ;
248248int core_fscache ;
249249
250+ int are_long_paths_enabled (void )
251+ {
252+ /* default to `false` during initialization */
253+ static const int fallback = 0 ;
254+
255+ static int enabled = -1 ;
256+
257+ if (enabled < 0 ) {
258+ /* avoid infinite recursion */
259+ if (!the_repository )
260+ return fallback ;
261+
262+ if (the_repository -> config &&
263+ the_repository -> config -> hash_initialized &&
264+ git_config_get_bool ("core.longpaths" , & enabled ) < 0 )
265+ enabled = 0 ;
266+ }
267+
268+ return enabled < 0 ? fallback : enabled ;
269+ }
270+
250271int mingw_core_config (const char * var , const char * value ,
251272 const struct config_context * ctx , void * cb )
252273{
@@ -311,8 +332,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
311332int mingw_unlink (const char * pathname )
312333{
313334 int ret , tries = 0 ;
314- wchar_t wpathname [MAX_PATH ];
315- if (xutftowcs_path (wpathname , pathname ) < 0 )
335+ wchar_t wpathname [MAX_LONG_PATH ];
336+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
316337 return -1 ;
317338
318339 if (DeleteFileW (wpathname ))
@@ -344,7 +365,7 @@ static int is_dir_empty(const wchar_t *wpath)
344365{
345366 WIN32_FIND_DATAW findbuf ;
346367 HANDLE handle ;
347- wchar_t wbuf [MAX_PATH + 2 ];
368+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
348369 wcscpy (wbuf , wpath );
349370 wcscat (wbuf , L"\\*" );
350371 handle = FindFirstFileW (wbuf , & findbuf );
@@ -365,7 +386,7 @@ static int is_dir_empty(const wchar_t *wpath)
365386int mingw_rmdir (const char * pathname )
366387{
367388 int ret , tries = 0 ;
368- wchar_t wpathname [MAX_PATH ];
389+ wchar_t wpathname [MAX_LONG_PATH ];
369390 struct stat st ;
370391
371392 /*
@@ -387,7 +408,7 @@ int mingw_rmdir(const char *pathname)
387408 return -1 ;
388409 }
389410
390- if (xutftowcs_path (wpathname , pathname ) < 0 )
411+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
391412 return -1 ;
392413
393414 while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -466,15 +487,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
466487int mingw_mkdir (const char * path , int mode )
467488{
468489 int ret ;
469- wchar_t wpath [MAX_PATH ];
490+ wchar_t wpath [MAX_LONG_PATH ];
470491
471492 if (!is_valid_win32_path (path , 0 )) {
472493 errno = EINVAL ;
473494 return -1 ;
474495 }
475496
476- if (xutftowcs_path (wpath , path ) < 0 )
497+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
498+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
499+ are_long_paths_enabled ()) < 0 )
477500 return -1 ;
501+
478502 ret = _wmkdir (wpath );
479503 if (!ret && needs_hiding (path ))
480504 return set_hidden_flag (wpath , 1 );
@@ -561,7 +585,7 @@ int mingw_open (const char *filename, int oflags, ...)
561585 va_list args ;
562586 unsigned mode ;
563587 int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
564- wchar_t wfilename [MAX_PATH ];
588+ wchar_t wfilename [MAX_LONG_PATH ];
565589 open_fn_t open_fn ;
566590
567591 va_start (args , oflags );
@@ -589,7 +613,7 @@ int mingw_open (const char *filename, int oflags, ...)
589613
590614 if (filename && !strcmp (filename , "/dev/null" ))
591615 wcscpy (wfilename , L"nul" );
592- else if (xutftowcs_path (wfilename , filename ) < 0 )
616+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
593617 return -1 ;
594618
595619 fd = open_fn (wfilename , oflags , mode );
@@ -647,14 +671,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
647671{
648672 int hide = needs_hiding (filename );
649673 FILE * file ;
650- wchar_t wfilename [MAX_PATH ], wotype [4 ];
674+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
651675 if (filename && !strcmp (filename , "/dev/null" ))
652676 wcscpy (wfilename , L"nul" );
653677 else if (!is_valid_win32_path (filename , 1 )) {
654678 int create = otype && strchr (otype , 'w' );
655679 errno = create ? EINVAL : ENOENT ;
656680 return NULL ;
657- } else if (xutftowcs_path (wfilename , filename ) < 0 )
681+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
658682 return NULL ;
659683
660684 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -676,14 +700,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
676700{
677701 int hide = needs_hiding (filename );
678702 FILE * file ;
679- wchar_t wfilename [MAX_PATH ], wotype [4 ];
703+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
680704 if (filename && !strcmp (filename , "/dev/null" ))
681705 wcscpy (wfilename , L"nul" );
682706 else if (!is_valid_win32_path (filename , 1 )) {
683707 int create = otype && strchr (otype , 'w' );
684708 errno = create ? EINVAL : ENOENT ;
685709 return NULL ;
686- } else if (xutftowcs_path (wfilename , filename ) < 0 )
710+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
687711 return NULL ;
688712
689713 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -733,7 +757,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
733757 HANDLE h = (HANDLE ) _get_osfhandle (fd );
734758 if (GetFileType (h ) != FILE_TYPE_PIPE ) {
735759 if (orig == EINVAL ) {
736- wchar_t path [MAX_PATH ];
760+ wchar_t path [MAX_LONG_PATH ];
737761 DWORD ret = GetFinalPathNameByHandleW (h , path ,
738762 ARRAY_SIZE (path ), 0 );
739763 UINT drive_type = ret > 0 && ret < ARRAY_SIZE (path ) ?
@@ -770,27 +794,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
770794
771795int mingw_access (const char * filename , int mode )
772796{
773- wchar_t wfilename [MAX_PATH ];
797+ wchar_t wfilename [MAX_LONG_PATH ];
774798 if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
775799 return 0 ;
776- if (xutftowcs_path (wfilename , filename ) < 0 )
800+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
777801 return -1 ;
778802 /* X_OK is not supported by the MSVCRT version */
779803 return _waccess (wfilename , mode & ~X_OK );
780804}
781805
806+ /* cached length of current directory for handle_long_path */
807+ static int current_directory_len = 0 ;
808+
782809int mingw_chdir (const char * dirname )
783810{
784- wchar_t wdirname [MAX_PATH ];
785- if (xutftowcs_path (wdirname , dirname ) < 0 )
811+ int result ;
812+ wchar_t wdirname [MAX_LONG_PATH ];
813+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
786814 return -1 ;
787- return _wchdir (wdirname );
815+ result = _wchdir (wdirname );
816+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
817+ return result ;
788818}
789819
790820int mingw_chmod (const char * filename , int mode )
791821{
792- wchar_t wfilename [MAX_PATH ];
793- if (xutftowcs_path (wfilename , filename ) < 0 )
822+ wchar_t wfilename [MAX_LONG_PATH ];
823+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
794824 return -1 ;
795825 return _wchmod (wfilename , mode );
796826}
@@ -838,8 +868,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
838868static int do_lstat (int follow , const char * file_name , struct stat * buf )
839869{
840870 WIN32_FILE_ATTRIBUTE_DATA fdata ;
841- wchar_t wfilename [MAX_PATH ];
842- if (xutftowcs_path (wfilename , file_name ) < 0 )
871+ wchar_t wfilename [MAX_LONG_PATH ];
872+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
843873 return -1 ;
844874
845875 if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -1010,10 +1040,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
10101040 FILETIME mft , aft ;
10111041 int rc ;
10121042 DWORD attrs ;
1013- wchar_t wfilename [MAX_PATH ];
1043+ wchar_t wfilename [MAX_LONG_PATH ];
10141044 HANDLE osfilehandle ;
10151045
1016- if (xutftowcs_path (wfilename , file_name ) < 0 )
1046+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
10171047 return -1 ;
10181048
10191049 /* must have write permission */
@@ -1096,6 +1126,7 @@ char *mingw_mktemp(char *template)
10961126 wchar_t wtemplate [MAX_PATH ];
10971127 int offset = 0 ;
10981128
1129+ /* we need to return the path, thus no long paths here! */
10991130 if (xutftowcs_path (wtemplate , template ) < 0 )
11001131 return NULL ;
11011132
@@ -1747,6 +1778,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
17471778
17481779 if (* argv && !strcmp (cmd , * argv ))
17491780 wcmd [0 ] = L'\0' ;
1781+ /*
1782+ * Paths to executables and to the current directory do not support
1783+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1784+ */
17501785 else if (xutftowcs_path (wcmd , cmd ) < 0 )
17511786 return -1 ;
17521787 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2395,8 +2430,9 @@ int mingw_rename(const char *pold, const char *pnew)
23952430{
23962431 DWORD attrs , gle ;
23972432 int tries = 0 ;
2398- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2399- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2433+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2434+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2435+ xutftowcs_long_path (wpnew , pnew ) < 0 )
24002436 return -1 ;
24012437
24022438 /*
@@ -2714,9 +2750,9 @@ int mingw_raise(int sig)
27142750
27152751int link (const char * oldpath , const char * newpath )
27162752{
2717- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2718- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2719- xutftowcs_path (wnewpath , newpath ) < 0 )
2753+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2754+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2755+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
27202756 return -1 ;
27212757
27222758 if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2784,8 +2820,8 @@ int mingw_is_mount_point(struct strbuf *path)
27842820{
27852821 WIN32_FIND_DATAW findbuf = { 0 };
27862822 HANDLE handle ;
2787- wchar_t wfilename [MAX_PATH ];
2788- int wlen = xutftowcs_path (wfilename , path -> buf );
2823+ wchar_t wfilename [MAX_LONG_PATH ];
2824+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
27892825 if (wlen < 0 )
27902826 die (_ ("could not get long path for '%s'" ), path -> buf );
27912827
@@ -2930,9 +2966,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
29302966
29312967static int is_system32_path (const char * path )
29322968{
2933- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
2969+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
29342970
2935- if (xutftowcs_path (wpath , path ) < 0 ||
2971+ if (xutftowcs_long_path (wpath , path ) < 0 ||
29362972 !GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
29372973 _wcsicmp (system32 , wpath ))
29382974 return 0 ;
@@ -3344,6 +3380,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
33443380 }
33453381}
33463382
3383+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3384+ {
3385+ int result ;
3386+ wchar_t buf [MAX_LONG_PATH ];
3387+
3388+ /*
3389+ * we don't need special handling if path is relative to the current
3390+ * directory, and current directory + path don't exceed the desired
3391+ * max_path limit. This should cover > 99 % of cases with minimal
3392+ * performance impact (git almost always uses relative paths).
3393+ */
3394+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3395+ (current_directory_len + len < max_path ))
3396+ return len ;
3397+
3398+ /*
3399+ * handle everything else:
3400+ * - absolute paths: "C:\dir\file"
3401+ * - absolute UNC paths: "\\server\share\dir\file"
3402+ * - absolute paths on current drive: "\dir\file"
3403+ * - relative paths on other drive: "X:file"
3404+ * - prefixed paths: "\\?\...", "\\.\..."
3405+ */
3406+
3407+ /* convert to absolute path using GetFullPathNameW */
3408+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3409+ if (!result ) {
3410+ errno = err_win_to_posix (GetLastError ());
3411+ return -1 ;
3412+ }
3413+
3414+ /*
3415+ * return absolute path if it fits within max_path (even if
3416+ * "cwd + path" doesn't due to '..' components)
3417+ */
3418+ if (result < max_path ) {
3419+ wcscpy (path , buf );
3420+ return result ;
3421+ }
3422+
3423+ /* error out if we shouldn't expand the path or buf is too small */
3424+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3425+ errno = ENAMETOOLONG ;
3426+ return -1 ;
3427+ }
3428+
3429+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3430+ if (buf [0 ] == '\\' ) {
3431+ /* ...unless already prefixed */
3432+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3433+ return len ;
3434+
3435+ wcscpy (path , L"\\\\?\\UNC\\" );
3436+ wcscpy (path + 8 , buf + 2 );
3437+ return result + 6 ;
3438+ } else {
3439+ wcscpy (path , L"\\\\?\\" );
3440+ wcscpy (path + 4 , buf );
3441+ return result + 4 ;
3442+ }
3443+ }
3444+
33473445#if !defined(_MSC_VER )
33483446/*
33493447 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3505,6 +3603,9 @@ int wmain(int argc, const wchar_t **wargv)
35053603 /* initialize Unicode console */
35063604 winansi_init ();
35073605
3606+ /* init length of current directory for handle_long_path */
3607+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3608+
35083609 /* invoke the real main() using our utf8 version of argv. */
35093610 exit_status = main (argc , argv );
35103611
0 commit comments