@@ -212,6 +212,7 @@ enum hide_dotfiles_type {
212
212
static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY ;
213
213
static char * unset_environment_variables ;
214
214
int core_fscache ;
215
+ int core_long_paths ;
215
216
216
217
int mingw_core_config (const char * var , const char * value )
217
218
{
@@ -228,6 +229,11 @@ int mingw_core_config(const char *var, const char *value)
228
229
return 0 ;
229
230
}
230
231
232
+ if (!strcmp (var , "core.longpaths" )) {
233
+ core_long_paths = git_config_bool (var , value );
234
+ return 0 ;
235
+ }
236
+
231
237
if (!strcmp (var , "core.unsetenvvars" )) {
232
238
free (unset_environment_variables );
233
239
unset_environment_variables = xstrdup (value );
@@ -240,8 +246,8 @@ int mingw_core_config(const char *var, const char *value)
240
246
int mingw_unlink (const char * pathname )
241
247
{
242
248
int ret , tries = 0 ;
243
- wchar_t wpathname [MAX_PATH ];
244
- if (xutftowcs_path (wpathname , pathname ) < 0 )
249
+ wchar_t wpathname [MAX_LONG_PATH ];
250
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
245
251
return -1 ;
246
252
247
253
/* read-only files cannot be removed */
@@ -270,7 +276,7 @@ static int is_dir_empty(const wchar_t *wpath)
270
276
{
271
277
WIN32_FIND_DATAW findbuf ;
272
278
HANDLE handle ;
273
- wchar_t wbuf [MAX_PATH + 2 ];
279
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
274
280
wcscpy (wbuf , wpath );
275
281
wcscat (wbuf , L"\\*" );
276
282
handle = FindFirstFileW (wbuf , & findbuf );
@@ -291,8 +297,8 @@ static int is_dir_empty(const wchar_t *wpath)
291
297
int mingw_rmdir (const char * pathname )
292
298
{
293
299
int ret , tries = 0 ;
294
- wchar_t wpathname [MAX_PATH ];
295
- if (xutftowcs_path (wpathname , pathname ) < 0 )
300
+ wchar_t wpathname [MAX_LONG_PATH ];
301
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
296
302
return -1 ;
297
303
298
304
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -367,9 +373,12 @@ static int set_hidden_flag(const wchar_t *path, int set)
367
373
int mingw_mkdir (const char * path , int mode )
368
374
{
369
375
int ret ;
370
- wchar_t wpath [MAX_PATH ];
371
- if (xutftowcs_path (wpath , path ) < 0 )
376
+ wchar_t wpath [MAX_LONG_PATH ];
377
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
378
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
379
+ core_long_paths ) < 0 )
372
380
return -1 ;
381
+
373
382
ret = _wmkdir (wpath );
374
383
if (!ret && needs_hiding (path ))
375
384
return set_hidden_flag (wpath , 1 );
@@ -381,7 +390,7 @@ int mingw_open (const char *filename, int oflags, ...)
381
390
va_list args ;
382
391
unsigned mode ;
383
392
int fd ;
384
- wchar_t wfilename [MAX_PATH ];
393
+ wchar_t wfilename [MAX_LONG_PATH ];
385
394
386
395
va_start (args , oflags );
387
396
mode = va_arg (args , int );
@@ -390,7 +399,7 @@ int mingw_open (const char *filename, int oflags, ...)
390
399
if (filename && !strcmp (filename , "/dev/null" ))
391
400
filename = "nul" ;
392
401
393
- if (xutftowcs_path (wfilename , filename ) < 0 )
402
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
394
403
return -1 ;
395
404
fd = _wopen (wfilename , oflags , mode );
396
405
@@ -447,10 +456,10 @@ FILE *mingw_fopen (const char *filename, const char *otype)
447
456
{
448
457
int hide = needs_hiding (filename );
449
458
FILE * file ;
450
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
459
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
451
460
if (filename && !strcmp (filename , "/dev/null" ))
452
461
filename = "nul" ;
453
- if (xutftowcs_path (wfilename , filename ) < 0 ||
462
+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
454
463
xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
455
464
return NULL ;
456
465
if (hide && !access (filename , F_OK ) && set_hidden_flag (wfilename , 0 )) {
@@ -467,10 +476,10 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
467
476
{
468
477
int hide = needs_hiding (filename );
469
478
FILE * file ;
470
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
479
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
471
480
if (filename && !strcmp (filename , "/dev/null" ))
472
481
filename = "nul" ;
473
- if (xutftowcs_path (wfilename , filename ) < 0 ||
482
+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
474
483
xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
475
484
return NULL ;
476
485
if (hide && !access (filename , F_OK ) && set_hidden_flag (wfilename , 0 )) {
@@ -524,25 +533,31 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
524
533
525
534
int mingw_access (const char * filename , int mode )
526
535
{
527
- wchar_t wfilename [MAX_PATH ];
528
- if (xutftowcs_path (wfilename , filename ) < 0 )
536
+ wchar_t wfilename [MAX_LONG_PATH ];
537
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
529
538
return -1 ;
530
539
/* X_OK is not supported by the MSVCRT version */
531
540
return _waccess (wfilename , mode & ~X_OK );
532
541
}
533
542
543
+ /* cached length of current directory for handle_long_path */
544
+ static int current_directory_len = 0 ;
545
+
534
546
int mingw_chdir (const char * dirname )
535
547
{
536
- wchar_t wdirname [MAX_PATH ];
537
- if (xutftowcs_path (wdirname , dirname ) < 0 )
548
+ int result ;
549
+ wchar_t wdirname [MAX_LONG_PATH ];
550
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
538
551
return -1 ;
539
- return _wchdir (wdirname );
552
+ result = _wchdir (wdirname );
553
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
554
+ return result ;
540
555
}
541
556
542
557
int mingw_chmod (const char * filename , int mode )
543
558
{
544
- wchar_t wfilename [MAX_PATH ];
545
- if (xutftowcs_path (wfilename , filename ) < 0 )
559
+ wchar_t wfilename [MAX_LONG_PATH ];
560
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
546
561
return -1 ;
547
562
return _wchmod (wfilename , mode );
548
563
}
@@ -590,8 +605,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
590
605
static int do_lstat (int follow , const char * file_name , struct stat * buf )
591
606
{
592
607
WIN32_FILE_ATTRIBUTE_DATA fdata ;
593
- wchar_t wfilename [MAX_PATH ];
594
- if (xutftowcs_path (wfilename , file_name ) < 0 )
608
+ wchar_t wfilename [MAX_LONG_PATH ];
609
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
595
610
return -1 ;
596
611
597
612
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -662,7 +677,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
662
677
static int do_stat_internal (int follow , const char * file_name , struct stat * buf )
663
678
{
664
679
int namelen ;
665
- char alt_name [PATH_MAX ];
680
+ char alt_name [MAX_LONG_PATH ];
666
681
667
682
if (!do_lstat (follow , file_name , buf ))
668
683
return 0 ;
@@ -678,7 +693,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
678
693
return -1 ;
679
694
while (namelen && file_name [namelen - 1 ] == '/' )
680
695
-- namelen ;
681
- if (!namelen || namelen >= PATH_MAX )
696
+ if (!namelen || namelen >= MAX_LONG_PATH )
682
697
return -1 ;
683
698
684
699
memcpy (alt_name , file_name , namelen );
@@ -740,8 +755,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
740
755
FILETIME mft , aft ;
741
756
int fh , rc ;
742
757
DWORD attrs ;
743
- wchar_t wfilename [MAX_PATH ];
744
- if (xutftowcs_path (wfilename , file_name ) < 0 )
758
+ wchar_t wfilename [MAX_LONG_PATH ];
759
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
745
760
return -1 ;
746
761
747
762
/* must have write permission */
@@ -789,6 +804,7 @@ unsigned int sleep (unsigned int seconds)
789
804
char * mingw_mktemp (char * template )
790
805
{
791
806
wchar_t wtemplate [MAX_PATH ];
807
+ /* we need to return the path, thus no long paths here! */
792
808
if (xutftowcs_path (wtemplate , template ) < 0 )
793
809
return NULL ;
794
810
if (!_wmktemp (wtemplate ))
@@ -1163,6 +1179,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1163
1179
si .hStdOutput = winansi_get_osfhandle (fhout );
1164
1180
si .hStdError = winansi_get_osfhandle (fherr );
1165
1181
1182
+ /* executables and the current directory don't support long paths */
1166
1183
if (xutftowcs_path (wcmd , cmd ) < 0 )
1167
1184
return -1 ;
1168
1185
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -1738,8 +1755,9 @@ int mingw_rename(const char *pold, const char *pnew)
1738
1755
{
1739
1756
DWORD attrs , gle ;
1740
1757
int tries = 0 ;
1741
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
1742
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
1758
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
1759
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
1760
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
1743
1761
return -1 ;
1744
1762
1745
1763
/*
@@ -1979,9 +1997,9 @@ int link(const char *oldpath, const char *newpath)
1979
1997
{
1980
1998
typedef BOOL (WINAPI * T )(LPCWSTR , LPCWSTR , LPSECURITY_ATTRIBUTES );
1981
1999
static T create_hard_link = NULL ;
1982
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
1983
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
1984
- xutftowcs_path (wnewpath , newpath ) < 0 )
2000
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2001
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2002
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
1985
2003
return -1 ;
1986
2004
1987
2005
if (!create_hard_link ) {
@@ -2194,6 +2212,68 @@ static void setup_windows_environment(void)
2194
2212
setenv ("TERM" , "cygwin" , 1 );
2195
2213
}
2196
2214
2215
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
2216
+ {
2217
+ int result ;
2218
+ wchar_t buf [MAX_LONG_PATH ];
2219
+
2220
+ /*
2221
+ * we don't need special handling if path is relative to the current
2222
+ * directory, and current directory + path don't exceed the desired
2223
+ * max_path limit. This should cover > 99 % of cases with minimal
2224
+ * performance impact (git almost always uses relative paths).
2225
+ */
2226
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
2227
+ (current_directory_len + len < max_path ))
2228
+ return len ;
2229
+
2230
+ /*
2231
+ * handle everything else:
2232
+ * - absolute paths: "C:\dir\file"
2233
+ * - absolute UNC paths: "\\server\share\dir\file"
2234
+ * - absolute paths on current drive: "\dir\file"
2235
+ * - relative paths on other drive: "X:file"
2236
+ * - prefixed paths: "\\?\...", "\\.\..."
2237
+ */
2238
+
2239
+ /* convert to absolute path using GetFullPathNameW */
2240
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
2241
+ if (!result ) {
2242
+ errno = err_win_to_posix (GetLastError ());
2243
+ return -1 ;
2244
+ }
2245
+
2246
+ /*
2247
+ * return absolute path if it fits within max_path (even if
2248
+ * "cwd + path" doesn't due to '..' components)
2249
+ */
2250
+ if (result < max_path ) {
2251
+ wcscpy (path , buf );
2252
+ return result ;
2253
+ }
2254
+
2255
+ /* error out if we shouldn't expand the path or buf is too small */
2256
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
2257
+ errno = ENAMETOOLONG ;
2258
+ return -1 ;
2259
+ }
2260
+
2261
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
2262
+ if (buf [0 ] == '\\' ) {
2263
+ /* ...unless already prefixed */
2264
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
2265
+ return len ;
2266
+
2267
+ wcscpy (path , L"\\\\?\\UNC\\" );
2268
+ wcscpy (path + 8 , buf + 2 );
2269
+ return result + 6 ;
2270
+ } else {
2271
+ wcscpy (path , L"\\\\?\\" );
2272
+ wcscpy (path + 4 , buf );
2273
+ return result + 4 ;
2274
+ }
2275
+ }
2276
+
2197
2277
/*
2198
2278
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
2199
2279
* mingw startup code, see init.c in mingw runtime).
@@ -2287,6 +2367,9 @@ void mingw_startup(void)
2287
2367
2288
2368
/* initialize Unicode console */
2289
2369
winansi_init ();
2370
+
2371
+ /* init length of current directory for handle_long_path */
2372
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
2290
2373
}
2291
2374
2292
2375
int uname (struct utsname * buf )
0 commit comments