@@ -249,6 +249,27 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
249249static  char  * unset_environment_variables ;
250250int  core_fscache ;
251251
252+ int  are_long_paths_enabled (void )
253+ {
254+ 	/* default to `false` during initialization */ 
255+ 	static  const  int  fallback  =  0 ;
256+ 
257+ 	static  int  enabled  =  -1 ;
258+ 
259+ 	if  (enabled  <  0 ) {
260+ 		/* avoid infinite recursion */ 
261+ 		if  (!the_repository )
262+ 			return  fallback ;
263+ 
264+ 		if  (the_repository -> config  && 
265+ 		    the_repository -> config -> hash_initialized  && 
266+ 		    git_config_get_bool ("core.longpaths" , & enabled ) <  0 )
267+ 			enabled  =  0 ;
268+ 	}
269+ 
270+ 	return  enabled  <  0  ? fallback  : enabled ;
271+ }
272+ 
252273int  mingw_core_config (const  char  * var , const  char  * value ,
253274		      const  struct  config_context  * ctx  UNUSED ,
254275		      void  * cb  UNUSED )
@@ -314,8 +335,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
314335int  mingw_unlink (const  char  * pathname )
315336{
316337	int  ret , tries  =  0 ;
317- 	wchar_t  wpathname [MAX_PATH ];
318- 	if  (xutftowcs_path (wpathname , pathname ) <  0 )
338+ 	wchar_t  wpathname [MAX_LONG_PATH ];
339+ 	if  (xutftowcs_long_path (wpathname , pathname ) <  0 )
319340		return  -1 ;
320341
321342	if  (DeleteFileW (wpathname ))
@@ -347,7 +368,7 @@ static int is_dir_empty(const wchar_t *wpath)
347368{
348369	WIN32_FIND_DATAW  findbuf ;
349370	HANDLE  handle ;
350- 	wchar_t  wbuf [MAX_PATH  +  2 ];
371+ 	wchar_t  wbuf [MAX_LONG_PATH  +  2 ];
351372	wcscpy (wbuf , wpath );
352373	wcscat (wbuf , L"\\*" );
353374	handle  =  FindFirstFileW (wbuf , & findbuf );
@@ -368,7 +389,7 @@ static int is_dir_empty(const wchar_t *wpath)
368389int  mingw_rmdir (const  char  * pathname )
369390{
370391	int  ret , tries  =  0 ;
371- 	wchar_t  wpathname [MAX_PATH ];
392+ 	wchar_t  wpathname [MAX_LONG_PATH ];
372393	struct  stat  st ;
373394
374395	/* 
@@ -390,7 +411,7 @@ int mingw_rmdir(const char *pathname)
390411		return  -1 ;
391412	}
392413
393- 	if  (xutftowcs_path (wpathname , pathname ) <  0 )
414+ 	if  (xutftowcs_long_path (wpathname , pathname ) <  0 )
394415		return  -1 ;
395416
396417	while  ((ret  =  _wrmdir (wpathname )) ==  -1  &&  tries  <  ARRAY_SIZE (delay )) {
@@ -469,15 +490,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
469490int  mingw_mkdir (const  char  * path , int  mode  UNUSED )
470491{
471492	int  ret ;
472- 	wchar_t  wpath [MAX_PATH ];
493+ 	wchar_t  wpath [MAX_LONG_PATH ];
473494
474495	if  (!is_valid_win32_path (path , 0 )) {
475496		errno  =  EINVAL ;
476497		return  -1 ;
477498	}
478499
479- 	if  (xutftowcs_path (wpath , path ) <  0 )
500+ 	/* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */ 
501+ 	if  (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
502+ 			      are_long_paths_enabled ()) <  0 )
480503		return  -1 ;
504+ 
481505	ret  =  _wmkdir (wpath );
482506	if  (!ret  &&  needs_hiding (path ))
483507		return  set_hidden_flag (wpath , 1 );
@@ -628,7 +652,7 @@ int mingw_open (const char *filename, int oflags, ...)
628652	va_list  args ;
629653	unsigned  mode ;
630654	int  fd , create  =  (oflags  &  (O_CREAT  | O_EXCL )) ==  (O_CREAT  | O_EXCL );
631- 	wchar_t  wfilename [MAX_PATH ];
655+ 	wchar_t  wfilename [MAX_LONG_PATH ];
632656	open_fn_t  open_fn ;
633657
634658	va_start (args , oflags );
@@ -658,7 +682,7 @@ int mingw_open (const char *filename, int oflags, ...)
658682
659683	if  (filename  &&  !strcmp (filename , "/dev/null" ))
660684		wcscpy (wfilename , L"nul" );
661- 	else  if  (xutftowcs_path (wfilename , filename ) <  0 )
685+ 	else  if  (xutftowcs_long_path (wfilename , filename ) <  0 )
662686		return  -1 ;
663687
664688	fd  =  open_fn (wfilename , oflags , mode );
@@ -716,14 +740,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
716740{
717741	int  hide  =  needs_hiding (filename );
718742	FILE  * file ;
719- 	wchar_t  wfilename [MAX_PATH ], wotype [4 ];
743+ 	wchar_t  wfilename [MAX_LONG_PATH ], wotype [4 ];
720744	if  (filename  &&  !strcmp (filename , "/dev/null" ))
721745		wcscpy (wfilename , L"nul" );
722746	else  if  (!is_valid_win32_path (filename , 1 )) {
723747		int  create  =  otype  &&  strchr (otype , 'w' );
724748		errno  =  create  ? EINVAL  : ENOENT ;
725749		return  NULL ;
726- 	} else  if  (xutftowcs_path (wfilename , filename ) <  0 )
750+ 	} else  if  (xutftowcs_long_path (wfilename , filename ) <  0 )
727751		return  NULL ;
728752
729753	if  (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) <  0 )
@@ -745,14 +769,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
745769{
746770	int  hide  =  needs_hiding (filename );
747771	FILE  * file ;
748- 	wchar_t  wfilename [MAX_PATH ], wotype [4 ];
772+ 	wchar_t  wfilename [MAX_LONG_PATH ], wotype [4 ];
749773	if  (filename  &&  !strcmp (filename , "/dev/null" ))
750774		wcscpy (wfilename , L"nul" );
751775	else  if  (!is_valid_win32_path (filename , 1 )) {
752776		int  create  =  otype  &&  strchr (otype , 'w' );
753777		errno  =  create  ? EINVAL  : ENOENT ;
754778		return  NULL ;
755- 	} else  if  (xutftowcs_path (wfilename , filename ) <  0 )
779+ 	} else  if  (xutftowcs_long_path (wfilename , filename ) <  0 )
756780		return  NULL ;
757781
758782	if  (xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) <  0 )
@@ -802,7 +826,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
802826		HANDLE  h  =  (HANDLE ) _get_osfhandle (fd );
803827		if  (GetFileType (h ) !=  FILE_TYPE_PIPE ) {
804828			if  (orig  ==  EINVAL ) {
805- 				wchar_t  path [MAX_PATH ];
829+ 				wchar_t  path [MAX_LONG_PATH ];
806830				DWORD  ret  =  GetFinalPathNameByHandleW (h , path ,
807831								ARRAY_SIZE (path ), 0 );
808832				UINT  drive_type  =  ret  >  0  &&  ret  <  ARRAY_SIZE (path ) ?
@@ -839,27 +863,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
839863
840864int  mingw_access (const  char  * filename , int  mode )
841865{
842- 	wchar_t  wfilename [MAX_PATH ];
866+ 	wchar_t  wfilename [MAX_LONG_PATH ];
843867	if  (!strcmp ("nul" , filename ) ||  !strcmp ("/dev/null" , filename ))
844868		return  0 ;
845- 	if  (xutftowcs_path (wfilename , filename ) <  0 )
869+ 	if  (xutftowcs_long_path (wfilename , filename ) <  0 )
846870		return  -1 ;
847871	/* X_OK is not supported by the MSVCRT version */ 
848872	return  _waccess (wfilename , mode  &  ~X_OK );
849873}
850874
875+ /* cached length of current directory for handle_long_path */ 
876+ static  int  current_directory_len  =  0 ;
877+ 
851878int  mingw_chdir (const  char  * dirname )
852879{
853- 	wchar_t  wdirname [MAX_PATH ];
854- 	if  (xutftowcs_path (wdirname , dirname ) <  0 )
880+ 	int  result ;
881+ 	wchar_t  wdirname [MAX_LONG_PATH ];
882+ 	if  (xutftowcs_long_path (wdirname , dirname ) <  0 )
855883		return  -1 ;
856- 	return  _wchdir (wdirname );
884+ 	result  =  _wchdir (wdirname );
885+ 	current_directory_len  =  GetCurrentDirectoryW (0 , NULL );
886+ 	return  result ;
857887}
858888
859889int  mingw_chmod (const  char  * filename , int  mode )
860890{
861- 	wchar_t  wfilename [MAX_PATH ];
862- 	if  (xutftowcs_path (wfilename , filename ) <  0 )
891+ 	wchar_t  wfilename [MAX_LONG_PATH ];
892+ 	if  (xutftowcs_long_path (wfilename , filename ) <  0 )
863893		return  -1 ;
864894	return  _wchmod (wfilename , mode );
865895}
@@ -907,8 +937,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
907937static  int  do_lstat (int  follow , const  char  * file_name , struct  stat  * buf )
908938{
909939	WIN32_FILE_ATTRIBUTE_DATA  fdata ;
910- 	wchar_t  wfilename [MAX_PATH ];
911- 	if  (xutftowcs_path (wfilename , file_name ) <  0 )
940+ 	wchar_t  wfilename [MAX_LONG_PATH ];
941+ 	if  (xutftowcs_long_path (wfilename , file_name ) <  0 )
912942		return  -1 ;
913943
914944	if  (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -1079,10 +1109,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
10791109	FILETIME  mft , aft ;
10801110	int  rc ;
10811111	DWORD  attrs ;
1082- 	wchar_t  wfilename [MAX_PATH ];
1112+ 	wchar_t  wfilename [MAX_LONG_PATH ];
10831113	HANDLE  osfilehandle ;
10841114
1085- 	if  (xutftowcs_path (wfilename , file_name ) <  0 )
1115+ 	if  (xutftowcs_long_path (wfilename , file_name ) <  0 )
10861116		return  -1 ;
10871117
10881118	/* must have write permission */ 
@@ -1165,6 +1195,7 @@ char *mingw_mktemp(char *template)
11651195	wchar_t  wtemplate [MAX_PATH ];
11661196	int  offset  =  0 ;
11671197
1198+ 	/* we need to return the path, thus no long paths here! */ 
11681199	if  (xutftowcs_path (wtemplate , template ) <  0 )
11691200		return  NULL ;
11701201
@@ -1817,6 +1848,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
18171848
18181849	if  (* argv  &&  !strcmp (cmd , * argv ))
18191850		wcmd [0 ] =  L'\0' ;
1851+ 	/* 
1852+ 	 * Paths to executables and to the current directory do not support 
1853+ 	 * long paths, therefore we cannot use xutftowcs_long_path() here. 
1854+ 	 */ 
18201855	else  if  (xutftowcs_path (wcmd , cmd ) <  0 )
18211856		return  -1 ;
18221857	if  (dir  &&  xutftowcs_path (wdir , dir ) <  0 )
@@ -2469,12 +2504,12 @@ int mingw_rename(const char *pold, const char *pnew)
24692504	static  int  supports_file_rename_info_ex  =  1 ;
24702505	DWORD  attrs , gle ;
24712506	int  tries  =  0 ;
2472- 	wchar_t  wpold [MAX_PATH ], wpnew [MAX_PATH ];
2507+ 	wchar_t  wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
24732508	int  wpnew_len ;
24742509
2475- 	if  (xutftowcs_path (wpold , pold ) <  0 )
2510+ 	if  (xutftowcs_long_path (wpold , pold ) <  0 )
24762511		return  -1 ;
2477- 	wpnew_len  =  xutftowcs_path (wpnew , pnew );
2512+ 	wpnew_len  =  xutftowcs_long_path (wpnew , pnew );
24782513	if  (wpnew_len  <  0 )
24792514		return  -1 ;
24802515
@@ -2866,9 +2901,9 @@ int mingw_raise(int sig)
28662901
28672902int  link (const  char  * oldpath , const  char  * newpath )
28682903{
2869- 	wchar_t  woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2870- 	if  (xutftowcs_path (woldpath , oldpath ) <  0  || 
2871- 		 xutftowcs_path (wnewpath , newpath ) <  0 )
2904+ 	wchar_t  woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2905+ 	if  (xutftowcs_long_path (woldpath , oldpath ) <  0  || 
2906+ 	     xutftowcs_long_path (wnewpath , newpath ) <  0 )
28722907		return  -1 ;
28732908
28742909	if  (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
@@ -2936,8 +2971,8 @@ int mingw_is_mount_point(struct strbuf *path)
29362971{
29372972	WIN32_FIND_DATAW  findbuf  =  { 0  };
29382973	HANDLE  handle ;
2939- 	wchar_t  wfilename [MAX_PATH ];
2940- 	int  wlen  =  xutftowcs_path (wfilename , path -> buf );
2974+ 	wchar_t  wfilename [MAX_LONG_PATH ];
2975+ 	int  wlen  =  xutftowcs_long_path (wfilename , path -> buf );
29412976	if  (wlen  <  0 )
29422977		die (_ ("could not get long path for '%s'" ), path -> buf );
29432978
@@ -3082,9 +3117,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
30823117
30833118static  int  is_system32_path (const  char  * path )
30843119{
3085- 	WCHAR  system32 [MAX_PATH ], wpath [MAX_PATH ];
3120+ 	WCHAR  system32 [MAX_LONG_PATH ], wpath [MAX_LONG_PATH ];
30863121
3087- 	if  (xutftowcs_path (wpath , path ) <  0  || 
3122+ 	if  (xutftowcs_long_path (wpath , path ) <  0  || 
30883123	    !GetSystemDirectoryW (system32 , ARRAY_SIZE (system32 )) || 
30893124	    _wcsicmp (system32 , wpath ))
30903125		return  0 ;
@@ -3496,6 +3531,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
34963531	}
34973532}
34983533
3534+ int  handle_long_path (wchar_t  * path , int  len , int  max_path , int  expand )
3535+ {
3536+ 	int  result ;
3537+ 	wchar_t  buf [MAX_LONG_PATH ];
3538+ 
3539+ 	/* 
3540+ 	 * we don't need special handling if path is relative to the current 
3541+ 	 * directory, and current directory + path don't exceed the desired 
3542+ 	 * max_path limit. This should cover > 99 % of cases with minimal 
3543+ 	 * performance impact (git almost always uses relative paths). 
3544+ 	 */ 
3545+ 	if  ((len  <  2  ||  (!is_dir_sep (path [0 ]) &&  path [1 ] !=  ':' )) && 
3546+ 	    (current_directory_len  +  len  <  max_path ))
3547+ 		return  len ;
3548+ 
3549+ 	/* 
3550+ 	 * handle everything else: 
3551+ 	 * - absolute paths: "C:\dir\file" 
3552+ 	 * - absolute UNC paths: "\\server\share\dir\file" 
3553+ 	 * - absolute paths on current drive: "\dir\file" 
3554+ 	 * - relative paths on other drive: "X:file" 
3555+ 	 * - prefixed paths: "\\?\...", "\\.\..." 
3556+ 	 */ 
3557+ 
3558+ 	/* convert to absolute path using GetFullPathNameW */ 
3559+ 	result  =  GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
3560+ 	if  (!result ) {
3561+ 		errno  =  err_win_to_posix (GetLastError ());
3562+ 		return  -1 ;
3563+ 	}
3564+ 
3565+ 	/* 
3566+ 	 * return absolute path if it fits within max_path (even if 
3567+ 	 * "cwd + path" doesn't due to '..' components) 
3568+ 	 */ 
3569+ 	if  (result  <  max_path ) {
3570+ 		wcscpy (path , buf );
3571+ 		return  result ;
3572+ 	}
3573+ 
3574+ 	/* error out if we shouldn't expand the path or buf is too small */ 
3575+ 	if  (!expand  ||  result  >= MAX_LONG_PATH  -  6 ) {
3576+ 		errno  =  ENAMETOOLONG ;
3577+ 		return  -1 ;
3578+ 	}
3579+ 
3580+ 	/* prefix full path with "\\?\" or "\\?\UNC\" */ 
3581+ 	if  (buf [0 ] ==  '\\' ) {
3582+ 		/* ...unless already prefixed */ 
3583+ 		if  (buf [1 ] ==  '\\'  &&  (buf [2 ] ==  '?'  ||  buf [2 ] ==  '.' ))
3584+ 			return  len ;
3585+ 
3586+ 		wcscpy (path , L"\\\\?\\UNC\\" );
3587+ 		wcscpy (path  +  8 , buf  +  2 );
3588+ 		return  result  +  6 ;
3589+ 	} else  {
3590+ 		wcscpy (path , L"\\\\?\\" );
3591+ 		wcscpy (path  +  4 , buf );
3592+ 		return  result  +  4 ;
3593+ 	}
3594+ }
3595+ 
34993596#if  !defined(_MSC_VER )
35003597/* 
35013598 * Disable MSVCRT command line wildcard expansion (__getmainargs called from 
@@ -3658,6 +3755,9 @@ int wmain(int argc, const wchar_t **wargv)
36583755	/* initialize Unicode console */ 
36593756	winansi_init ();
36603757
3758+ 	/* init length of current directory for handle_long_path */ 
3759+ 	current_directory_len  =  GetCurrentDirectoryW (0 , NULL );
3760+ 
36613761	/* invoke the real main() using our utf8 version of argv. */ 
36623762	exit_status  =  main (argc , argv );
36633763
0 commit comments