@@ -213,6 +213,7 @@ enum hide_dotfiles_type {
213
213
static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY ;
214
214
static char * unset_environment_variables ;
215
215
int core_fscache ;
216
+ int core_long_paths ;
216
217
217
218
int mingw_core_config (const char * var , const char * value , void * cb )
218
219
{
@@ -229,6 +230,11 @@ int mingw_core_config(const char *var, const char *value, void *cb)
229
230
return 0 ;
230
231
}
231
232
233
+ if (!strcmp (var , "core.longpaths" )) {
234
+ core_long_paths = git_config_bool (var , value );
235
+ return 0 ;
236
+ }
237
+
232
238
if (!strcmp (var , "core.unsetenvvars" )) {
233
239
free (unset_environment_variables );
234
240
unset_environment_variables = xstrdup (value );
@@ -266,8 +272,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
266
272
int mingw_unlink (const char * pathname )
267
273
{
268
274
int ret , tries = 0 ;
269
- wchar_t wpathname [MAX_PATH ];
270
- if (xutftowcs_path (wpathname , pathname ) < 0 )
275
+ wchar_t wpathname [MAX_LONG_PATH ];
276
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
271
277
return -1 ;
272
278
273
279
/* read-only files cannot be removed */
@@ -296,7 +302,7 @@ static int is_dir_empty(const wchar_t *wpath)
296
302
{
297
303
WIN32_FIND_DATAW findbuf ;
298
304
HANDLE handle ;
299
- wchar_t wbuf [MAX_PATH + 2 ];
305
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
300
306
wcscpy (wbuf , wpath );
301
307
wcscat (wbuf , L"\\*" );
302
308
handle = FindFirstFileW (wbuf , & findbuf );
@@ -317,8 +323,8 @@ static int is_dir_empty(const wchar_t *wpath)
317
323
int mingw_rmdir (const char * pathname )
318
324
{
319
325
int ret , tries = 0 ;
320
- wchar_t wpathname [MAX_PATH ];
321
- if (xutftowcs_path (wpathname , pathname ) < 0 )
326
+ wchar_t wpathname [MAX_LONG_PATH ];
327
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
322
328
return -1 ;
323
329
324
330
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -393,9 +399,12 @@ static int set_hidden_flag(const wchar_t *path, int set)
393
399
int mingw_mkdir (const char * path , int mode )
394
400
{
395
401
int ret ;
396
- wchar_t wpath [MAX_PATH ];
397
- if (xutftowcs_path (wpath , path ) < 0 )
402
+ wchar_t wpath [MAX_LONG_PATH ];
403
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
404
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
405
+ core_long_paths ) < 0 )
398
406
return -1 ;
407
+
399
408
ret = _wmkdir (wpath );
400
409
if (!ret && needs_hiding (path ))
401
410
return set_hidden_flag (wpath , 1 );
@@ -438,7 +447,7 @@ int mingw_open (const char *filename, int oflags, ...)
438
447
va_list args ;
439
448
unsigned mode ;
440
449
int fd ;
441
- wchar_t wfilename [MAX_PATH ];
450
+ wchar_t wfilename [MAX_LONG_PATH ];
442
451
open_fn_t open_fn ;
443
452
444
453
va_start (args , oflags );
@@ -453,7 +462,7 @@ int mingw_open (const char *filename, int oflags, ...)
453
462
else
454
463
open_fn = _wopen ;
455
464
456
- if (xutftowcs_path (wfilename , filename ) < 0 )
465
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
457
466
return -1 ;
458
467
fd = open_fn (wfilename , oflags , mode );
459
468
@@ -510,10 +519,10 @@ FILE *mingw_fopen (const char *filename, const char *otype)
510
519
{
511
520
int hide = needs_hiding (filename );
512
521
FILE * file ;
513
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
522
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
514
523
if (filename && !strcmp (filename , "/dev/null" ))
515
524
filename = "nul" ;
516
- if (xutftowcs_path (wfilename , filename ) < 0 ||
525
+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
517
526
xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
518
527
return NULL ;
519
528
if (hide && !access (filename , F_OK ) && set_hidden_flag (wfilename , 0 )) {
@@ -532,10 +541,10 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
532
541
{
533
542
int hide = needs_hiding (filename );
534
543
FILE * file ;
535
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
544
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
536
545
if (filename && !strcmp (filename , "/dev/null" ))
537
546
filename = "nul" ;
538
- if (xutftowcs_path (wfilename , filename ) < 0 ||
547
+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
539
548
xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
540
549
return NULL ;
541
550
if (hide && !access (filename , F_OK ) && set_hidden_flag (wfilename , 0 )) {
@@ -589,25 +598,31 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
589
598
590
599
int mingw_access (const char * filename , int mode )
591
600
{
592
- wchar_t wfilename [MAX_PATH ];
593
- if (xutftowcs_path (wfilename , filename ) < 0 )
601
+ wchar_t wfilename [MAX_LONG_PATH ];
602
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
594
603
return -1 ;
595
604
/* X_OK is not supported by the MSVCRT version */
596
605
return _waccess (wfilename , mode & ~X_OK );
597
606
}
598
607
608
+ /* cached length of current directory for handle_long_path */
609
+ static int current_directory_len = 0 ;
610
+
599
611
int mingw_chdir (const char * dirname )
600
612
{
601
- wchar_t wdirname [MAX_PATH ];
602
- if (xutftowcs_path (wdirname , dirname ) < 0 )
613
+ int result ;
614
+ wchar_t wdirname [MAX_LONG_PATH ];
615
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
603
616
return -1 ;
604
- return _wchdir (wdirname );
617
+ result = _wchdir (wdirname );
618
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
619
+ return result ;
605
620
}
606
621
607
622
int mingw_chmod (const char * filename , int mode )
608
623
{
609
- wchar_t wfilename [MAX_PATH ];
610
- if (xutftowcs_path (wfilename , filename ) < 0 )
624
+ wchar_t wfilename [MAX_LONG_PATH ];
625
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
611
626
return -1 ;
612
627
return _wchmod (wfilename , mode );
613
628
}
@@ -655,8 +670,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
655
670
static int do_lstat (int follow , const char * file_name , struct stat * buf )
656
671
{
657
672
WIN32_FILE_ATTRIBUTE_DATA fdata ;
658
- wchar_t wfilename [MAX_PATH ];
659
- if (xutftowcs_path (wfilename , file_name ) < 0 )
673
+ wchar_t wfilename [MAX_LONG_PATH ];
674
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
660
675
return -1 ;
661
676
662
677
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -727,7 +742,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
727
742
static int do_stat_internal (int follow , const char * file_name , struct stat * buf )
728
743
{
729
744
int namelen ;
730
- char alt_name [PATH_MAX ];
745
+ char alt_name [MAX_LONG_PATH ];
731
746
732
747
if (!do_lstat (follow , file_name , buf ))
733
748
return 0 ;
@@ -743,7 +758,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
743
758
return -1 ;
744
759
while (namelen && file_name [namelen - 1 ] == '/' )
745
760
-- namelen ;
746
- if (!namelen || namelen >= PATH_MAX )
761
+ if (!namelen || namelen >= MAX_LONG_PATH )
747
762
return -1 ;
748
763
749
764
memcpy (alt_name , file_name , namelen );
@@ -827,8 +842,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
827
842
FILETIME mft , aft ;
828
843
int fh , rc ;
829
844
DWORD attrs ;
830
- wchar_t wfilename [MAX_PATH ];
831
- if (xutftowcs_path (wfilename , file_name ) < 0 )
845
+ wchar_t wfilename [MAX_LONG_PATH ];
846
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
832
847
return -1 ;
833
848
834
849
/* must have write permission */
@@ -887,11 +902,20 @@ unsigned int sleep (unsigned int seconds)
887
902
char * mingw_mktemp (char * template )
888
903
{
889
904
wchar_t wtemplate [MAX_PATH ];
905
+ int offset = 0 ;
906
+
907
+ /* we need to return the path, thus no long paths here! */
890
908
if (xutftowcs_path (wtemplate , template ) < 0 )
891
909
return NULL ;
910
+
911
+ if (is_dir_sep (template [0 ]) && !is_dir_sep (template [1 ]) &&
912
+ iswalpha (wtemplate [0 ]) && wtemplate [1 ] == L':' ) {
913
+ /* We have an absolute path missing the drive prefix */
914
+ offset = 2 ;
915
+ }
892
916
if (!_wmktemp (wtemplate ))
893
917
return NULL ;
894
- if (xwcstoutf (template , wtemplate , strlen (template ) + 1 ) < 0 )
918
+ if (xwcstoutf (template , wtemplate + offset , strlen (template ) + 1 ) < 0 )
895
919
return NULL ;
896
920
return template ;
897
921
}
@@ -1321,6 +1345,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1321
1345
si .hStdOutput = winansi_get_osfhandle (fhout );
1322
1346
si .hStdError = winansi_get_osfhandle (fherr );
1323
1347
1348
+ /* executables and the current directory don't support long paths */
1324
1349
if (xutftowcs_path (wcmd , cmd ) < 0 )
1325
1350
return -1 ;
1326
1351
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -1888,8 +1913,9 @@ int mingw_rename(const char *pold, const char *pnew)
1888
1913
{
1889
1914
DWORD attrs , gle ;
1890
1915
int tries = 0 ;
1891
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
1892
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
1916
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
1917
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
1918
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
1893
1919
return -1 ;
1894
1920
1895
1921
/*
@@ -2153,24 +2179,12 @@ int mingw_raise(int sig)
2153
2179
2154
2180
int link (const char * oldpath , const char * newpath )
2155
2181
{
2156
- typedef BOOL (WINAPI * T )(LPCWSTR , LPCWSTR , LPSECURITY_ATTRIBUTES );
2157
- static T create_hard_link = NULL ;
2158
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
2159
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
2160
- xutftowcs_path (wnewpath , newpath ) < 0 )
2182
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
2183
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
2184
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
2161
2185
return -1 ;
2162
2186
2163
- if (!create_hard_link ) {
2164
- create_hard_link = (T ) GetProcAddress (
2165
- GetModuleHandle ("kernel32.dll" ), "CreateHardLinkW" );
2166
- if (!create_hard_link )
2167
- create_hard_link = (T )- 1 ;
2168
- }
2169
- if (create_hard_link == (T )- 1 ) {
2170
- errno = ENOSYS ;
2171
- return -1 ;
2172
- }
2173
- if (!create_hard_link (wnewpath , woldpath , NULL )) {
2187
+ if (!CreateHardLinkW (wnewpath , woldpath , NULL )) {
2174
2188
errno = err_win_to_posix (GetLastError ());
2175
2189
return -1 ;
2176
2190
}
@@ -2370,6 +2384,68 @@ static void setup_windows_environment(void)
2370
2384
setenv ("TERM" , "cygwin" , 1 );
2371
2385
}
2372
2386
2387
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
2388
+ {
2389
+ int result ;
2390
+ wchar_t buf [MAX_LONG_PATH ];
2391
+
2392
+ /*
2393
+ * we don't need special handling if path is relative to the current
2394
+ * directory, and current directory + path don't exceed the desired
2395
+ * max_path limit. This should cover > 99 % of cases with minimal
2396
+ * performance impact (git almost always uses relative paths).
2397
+ */
2398
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
2399
+ (current_directory_len + len < max_path ))
2400
+ return len ;
2401
+
2402
+ /*
2403
+ * handle everything else:
2404
+ * - absolute paths: "C:\dir\file"
2405
+ * - absolute UNC paths: "\\server\share\dir\file"
2406
+ * - absolute paths on current drive: "\dir\file"
2407
+ * - relative paths on other drive: "X:file"
2408
+ * - prefixed paths: "\\?\...", "\\.\..."
2409
+ */
2410
+
2411
+ /* convert to absolute path using GetFullPathNameW */
2412
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
2413
+ if (!result ) {
2414
+ errno = err_win_to_posix (GetLastError ());
2415
+ return -1 ;
2416
+ }
2417
+
2418
+ /*
2419
+ * return absolute path if it fits within max_path (even if
2420
+ * "cwd + path" doesn't due to '..' components)
2421
+ */
2422
+ if (result < max_path ) {
2423
+ wcscpy (path , buf );
2424
+ return result ;
2425
+ }
2426
+
2427
+ /* error out if we shouldn't expand the path or buf is too small */
2428
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
2429
+ errno = ENAMETOOLONG ;
2430
+ return -1 ;
2431
+ }
2432
+
2433
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
2434
+ if (buf [0 ] == '\\' ) {
2435
+ /* ...unless already prefixed */
2436
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
2437
+ return len ;
2438
+
2439
+ wcscpy (path , L"\\\\?\\UNC\\" );
2440
+ wcscpy (path + 8 , buf + 2 );
2441
+ return result + 6 ;
2442
+ } else {
2443
+ wcscpy (path , L"\\\\?\\" );
2444
+ wcscpy (path + 4 , buf );
2445
+ return result + 4 ;
2446
+ }
2447
+ }
2448
+
2373
2449
/*
2374
2450
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
2375
2451
* mingw startup code, see init.c in mingw runtime).
@@ -2534,6 +2610,9 @@ int wmain(int argc, const wchar_t **wargv)
2534
2610
/* initialize Unicode console */
2535
2611
winansi_init ();
2536
2612
2613
+ /* init length of current directory for handle_long_path */
2614
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
2615
+
2537
2616
/* invoke the real main() using our utf8 version of argv. */
2538
2617
exit_status = main (argc , argv );
2539
2618
0 commit comments