@@ -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{
@@ -309,8 +330,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
309330int mingw_unlink (const char * pathname )
310331{
311332 int ret , tries = 0 ;
312- wchar_t wpathname [MAX_PATH ];
313- if (xutftowcs_path (wpathname , pathname ) < 0 )
333+ wchar_t wpathname [MAX_LONG_PATH ];
334+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
314335 return -1 ;
315336
316337 if (DeleteFileW (wpathname ))
@@ -342,7 +363,7 @@ static int is_dir_empty(const wchar_t *wpath)
342363{
343364 WIN32_FIND_DATAW findbuf ;
344365 HANDLE handle ;
345- wchar_t wbuf [MAX_PATH + 2 ];
366+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
346367 wcscpy (wbuf , wpath );
347368 wcscat (wbuf , L"\\*" );
348369 handle = FindFirstFileW (wbuf , & findbuf );
@@ -363,7 +384,7 @@ static int is_dir_empty(const wchar_t *wpath)
363384int mingw_rmdir (const char * pathname )
364385{
365386 int ret , tries = 0 ;
366- wchar_t wpathname [MAX_PATH ];
387+ wchar_t wpathname [MAX_LONG_PATH ];
367388 struct stat st ;
368389
369390 /*
@@ -385,7 +406,7 @@ int mingw_rmdir(const char *pathname)
385406 return -1 ;
386407 }
387408
388- if (xutftowcs_path (wpathname , pathname ) < 0 )
409+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
389410 return -1 ;
390411
391412 while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -464,15 +485,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
464485int mingw_mkdir (const char * path , int mode )
465486{
466487 int ret ;
467- wchar_t wpath [MAX_PATH ];
488+ wchar_t wpath [MAX_LONG_PATH ];
468489
469490 if (!is_valid_win32_path (path , 0 )) {
470491 errno = EINVAL ;
471492 return -1 ;
472493 }
473494
474- if (xutftowcs_path (wpath , path ) < 0 )
495+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
496+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
497+ are_long_paths_enabled ()) < 0 )
475498 return -1 ;
499+
476500 ret = _wmkdir (wpath );
477501 if (!ret && needs_hiding (path ))
478502 return set_hidden_flag (wpath , 1 );
@@ -559,7 +583,7 @@ int mingw_open (const char *filename, int oflags, ...)
559583 va_list args ;
560584 unsigned mode ;
561585 int fd , create = (oflags & (O_CREAT | O_EXCL )) == (O_CREAT | O_EXCL );
562- wchar_t wfilename [MAX_PATH ];
586+ wchar_t wfilename [MAX_LONG_PATH ];
563587 open_fn_t open_fn ;
564588
565589 va_start (args , oflags );
@@ -587,7 +611,7 @@ int mingw_open (const char *filename, int oflags, ...)
587611
588612 if (filename && !strcmp (filename , "/dev/null" ))
589613 wcscpy (wfilename , L"nul" );
590- else if (xutftowcs_path (wfilename , filename ) < 0 )
614+ else if (xutftowcs_long_path (wfilename , filename ) < 0 )
591615 return -1 ;
592616
593617 fd = open_fn (wfilename , oflags , mode );
@@ -645,14 +669,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
645669{
646670 int hide = needs_hiding (filename );
647671 FILE * file ;
648- wchar_t wfilename [MAX_PATH ], wotype [4 ];
672+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
649673 if (filename && !strcmp (filename , "/dev/null" ))
650674 wcscpy (wfilename , L"nul" );
651675 else if (!is_valid_win32_path (filename , 1 )) {
652676 int create = otype && strchr (otype , 'w' );
653677 errno = create ? EINVAL : ENOENT ;
654678 return NULL ;
655- } else if (xutftowcs_path (wfilename , filename ) < 0 )
679+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
656680 return NULL ;
657681
658682 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -674,14 +698,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
674698{
675699 int hide = needs_hiding (filename );
676700 FILE * file ;
677- wchar_t wfilename [MAX_PATH ], wotype [4 ];
701+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
678702 if (filename && !strcmp (filename , "/dev/null" ))
679703 wcscpy (wfilename , L"nul" );
680704 else if (!is_valid_win32_path (filename , 1 )) {
681705 int create = otype && strchr (otype , 'w' );
682706 errno = create ? EINVAL : ENOENT ;
683707 return NULL ;
684- } else if (xutftowcs_path (wfilename , filename ) < 0 )
708+ } else if (xutftowcs_long_path (wfilename , filename ) < 0 )
685709 return NULL ;
686710
687711 if (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
@@ -730,7 +754,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
730754 if (GetFileType (h ) == FILE_TYPE_PIPE )
731755 errno = EPIPE ;
732756 else {
733- wchar_t path [MAX_PATH ];
757+ wchar_t path [MAX_LONG_PATH ];
734758 DWORD ret = GetFinalPathNameByHandleW (h , path ,
735759 ARRAY_SIZE (path ), 0 );
736760 UINT drive_type = ret > 0 && ret < ARRAY_SIZE (path ) ?
@@ -756,27 +780,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
756780
757781int mingw_access (const char * filename , int mode )
758782{
759- wchar_t wfilename [MAX_PATH ];
783+ wchar_t wfilename [MAX_LONG_PATH ];
760784 if (!strcmp ("nul" , filename ) || !strcmp ("/dev/null" , filename ))
761785 return 0 ;
762- if (xutftowcs_path (wfilename , filename ) < 0 )
786+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
763787 return -1 ;
764788 /* X_OK is not supported by the MSVCRT version */
765789 return _waccess (wfilename , mode & ~X_OK );
766790}
767791
792+ /* cached length of current directory for handle_long_path */
793+ static int current_directory_len = 0 ;
794+
768795int mingw_chdir (const char * dirname )
769796{
770- wchar_t wdirname [MAX_PATH ];
771- if (xutftowcs_path (wdirname , dirname ) < 0 )
797+ int result ;
798+ wchar_t wdirname [MAX_LONG_PATH ];
799+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
772800 return -1 ;
773- return _wchdir (wdirname );
801+ result = _wchdir (wdirname );
802+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
803+ return result ;
774804}
775805
776806int mingw_chmod (const char * filename , int mode )
777807{
778- wchar_t wfilename [MAX_PATH ];
779- if (xutftowcs_path (wfilename , filename ) < 0 )
808+ wchar_t wfilename [MAX_LONG_PATH ];
809+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
780810 return -1 ;
781811 return _wchmod (wfilename , mode );
782812}
@@ -824,8 +854,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
824854static int do_lstat (int follow , const char * file_name , struct stat * buf )
825855{
826856 WIN32_FILE_ATTRIBUTE_DATA fdata ;
827- wchar_t wfilename [MAX_PATH ];
828- if (xutftowcs_path (wfilename , file_name ) < 0 )
857+ wchar_t wfilename [MAX_LONG_PATH ];
858+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
829859 return -1 ;
830860
831861 if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -896,7 +926,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
896926static int do_stat_internal (int follow , const char * file_name , struct stat * buf )
897927{
898928 int namelen ;
899- char alt_name [PATH_MAX ];
929+ char alt_name [MAX_LONG_PATH ];
900930
901931 if (!do_lstat (follow , file_name , buf ))
902932 return 0 ;
@@ -912,7 +942,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
912942 return -1 ;
913943 while (namelen && file_name [namelen - 1 ] == '/' )
914944 -- namelen ;
915- if (!namelen || namelen >= PATH_MAX )
945+ if (!namelen || namelen >= MAX_LONG_PATH )
916946 return -1 ;
917947
918948 memcpy (alt_name , file_name , namelen );
@@ -996,10 +1026,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
9961026 FILETIME mft , aft ;
9971027 int rc ;
9981028 DWORD attrs ;
999- wchar_t wfilename [MAX_PATH ];
1029+ wchar_t wfilename [MAX_LONG_PATH ];
10001030 HANDLE osfilehandle ;
10011031
1002- if (xutftowcs_path (wfilename , file_name ) < 0 )
1032+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
10031033 return -1 ;
10041034
10051035 /* must have write permission */
@@ -1082,6 +1112,7 @@ char *mingw_mktemp(char *template)
10821112 wchar_t wtemplate [MAX_PATH ];
10831113 int offset = 0 ;
10841114
1115+ /* we need to return the path, thus no long paths here! */
10851116 if (xutftowcs_path (wtemplate , template ) < 0 )
10861117 return NULL ;
10871118
@@ -1733,6 +1764,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
17331764
17341765 if (* argv && !strcmp (cmd , * argv ))
17351766 wcmd [0 ] = L'\0' ;
1767+ /*
1768+ * Paths to executables and to the current directory do not support
1769+ * long paths, therefore we cannot use xutftowcs_long_path() here.
1770+ */
17361771 else if (xutftowcs_path (wcmd , cmd ) < 0 )
17371772 return -1 ;
17381773 if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -2381,8 +2416,9 @@ int mingw_rename(const char *pold, const char *pnew)
23812416{
23822417 DWORD attrs , gle ;
23832418 int tries = 0 ;
2384- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
2385- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
2419+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
2420+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
2421+ xutftowcs_long_path (wpnew , pnew ) < 0 )
23862422 return -1 ;
23872423
23882424 /*
@@ -2696,9 +2732,9 @@ int mingw_raise(int sig)
26962732
26972733int link (const char * oldpath , const char * newpath )
26982734{
2699- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2700- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2701- xutftowcs_path (wnewpath , newpath ) < 0 )
2735+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2736+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2737+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
27022738 return -1 ;
27032739
27042740 if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2766,8 +2802,8 @@ int mingw_is_mount_point(struct strbuf *path)
27662802{
27672803 WIN32_FIND_DATAW findbuf = { 0 };
27682804 HANDLE handle ;
2769- wchar_t wfilename [MAX_PATH ];
2770- int wlen = xutftowcs_path (wfilename , path -> buf );
2805+ wchar_t wfilename [MAX_LONG_PATH ];
2806+ int wlen = xutftowcs_long_path (wfilename , path -> buf );
27712807 if (wlen < 0 )
27722808 die (_ ("could not get long path for '%s'" ), path -> buf );
27732809
@@ -2912,9 +2948,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
29122948
29132949static int is_system32_path (const char * path )
29142950{
2915- WCHAR system32 [MAX_PATH ], wpath [MAX_PATH ];
2951+ WCHAR system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
29162952
2917- if (xutftowcs_path (wpath , path ) < 0 ||
2953+ if (xutftowcs_long_path (wpath , path ) < 0 ||
29182954 !GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) ||
29192955 _wcsicmp (system32 , wpath ))
29202956 return 0 ;
@@ -3282,6 +3318,73 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
32823318 }
32833319}
32843320
3321+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
3322+ {
3323+ int result ;
3324+ wchar_t buf [MAX_LONG_PATH ];
3325+
3326+ /*
3327+ * we don't need special handling if path is relative to the current
3328+ * directory, and current directory + path don't exceed the desired
3329+ * max_path limit. This should cover > 99 % of cases with minimal
3330+ * performance impact (git almost always uses relative paths).
3331+ */
3332+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
3333+ (current_directory_len + len < max_path ))
3334+ return len ;
3335+
3336+ /*
3337+ * handle everything else:
3338+ * - absolute paths: "C:\dir\file"
3339+ * - absolute UNC paths: "\\server\share\dir\file"
3340+ * - absolute paths on current drive: "\dir\file"
3341+ * - relative paths on other drive: "X:file"
3342+ * - prefixed paths: "\\?\...", "\\.\..."
3343+ */
3344+
3345+ /* convert to absolute path using GetFullPathNameW */
3346+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3347+ if (!result ) {
3348+ errno = err_win_to_posix (GetLastError ());
3349+ return -1 ;
3350+ }
3351+
3352+ /*
3353+ * return absolute path if it fits within max_path (even if
3354+ * "cwd + path" doesn't due to '..' components)
3355+ */
3356+ if (result < max_path ) {
3357+ /* Be careful not to add a drive prefix if there was none */
3358+ if (is_wdir_sep (path [0 ]) &&
3359+ !is_wdir_sep (buf [0 ]) && buf [1 ] == L':' && is_wdir_sep (buf [2 ]))
3360+ wcscpy (path , buf + 2 );
3361+ else
3362+ wcscpy (path , buf );
3363+ return result ;
3364+ }
3365+
3366+ /* error out if we shouldn't expand the path or buf is too small */
3367+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
3368+ errno = ENAMETOOLONG ;
3369+ return -1 ;
3370+ }
3371+
3372+ /* prefix full path with "\\?\" or "\\?\UNC\" */
3373+ if (buf [0 ] == '\\' ) {
3374+ /* ...unless already prefixed */
3375+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
3376+ return len ;
3377+
3378+ wcscpy (path , L"\\\\?\\UNC\\" );
3379+ wcscpy (path + 8 , buf + 2 );
3380+ return result + 6 ;
3381+ } else {
3382+ wcscpy (path , L"\\\\?\\" );
3383+ wcscpy (path + 4 , buf );
3384+ return result + 4 ;
3385+ }
3386+ }
3387+
32853388#if !defined(_MSC_VER )
32863389/*
32873390 * Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3443,6 +3546,9 @@ int wmain(int argc, const wchar_t **wargv)
34433546 /* initialize Unicode console */
34443547 winansi_init ();
34453548
3549+ /* init length of current directory for handle_long_path */
3550+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
3551+
34463552 /* invoke the real main() using our utf8 version of argv. */
34473553 exit_status = main (argc , argv );
34483554
0 commit comments