@@ -204,8 +204,8 @@ static int ask_yes_no_if_possible(const char *format, ...)
204
204
int mingw_unlink (const char * pathname )
205
205
{
206
206
int ret , tries = 0 ;
207
- wchar_t wpathname [MAX_PATH ];
208
- if (xutftowcs_path (wpathname , pathname ) < 0 )
207
+ wchar_t wpathname [MAX_LONG_PATH ];
208
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
209
209
return -1 ;
210
210
211
211
/* read-only files cannot be removed */
@@ -234,7 +234,7 @@ static int is_dir_empty(const wchar_t *wpath)
234
234
{
235
235
WIN32_FIND_DATAW findbuf ;
236
236
HANDLE handle ;
237
- wchar_t wbuf [MAX_PATH + 2 ];
237
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
238
238
wcscpy (wbuf , wpath );
239
239
wcscat (wbuf , L"\\*" );
240
240
handle = FindFirstFileW (wbuf , & findbuf );
@@ -255,8 +255,8 @@ static int is_dir_empty(const wchar_t *wpath)
255
255
int mingw_rmdir (const char * pathname )
256
256
{
257
257
int ret , tries = 0 ;
258
- wchar_t wpathname [MAX_PATH ];
259
- if (xutftowcs_path (wpathname , pathname ) < 0 )
258
+ wchar_t wpathname [MAX_LONG_PATH ];
259
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
260
260
return -1 ;
261
261
262
262
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -296,9 +296,9 @@ static int make_hidden(const wchar_t *path)
296
296
297
297
void mingw_mark_as_git_dir (const char * dir )
298
298
{
299
- wchar_t wdir [MAX_PATH ];
299
+ wchar_t wdir [MAX_LONG_PATH ];
300
300
if (hide_dotfiles != HIDE_DOTFILES_FALSE && !is_bare_repository ())
301
- if (xutftowcs_path (wdir , dir ) < 0 || make_hidden (wdir ))
301
+ if (xutftowcs_long_path (wdir , dir ) < 0 || make_hidden (wdir ))
302
302
warning ("Failed to make '%s' hidden" , dir );
303
303
git_config_set ("core.hideDotFiles" ,
304
304
hide_dotfiles == HIDE_DOTFILES_FALSE ? "false" :
@@ -309,9 +309,12 @@ void mingw_mark_as_git_dir(const char *dir)
309
309
int mingw_mkdir (const char * path , int mode )
310
310
{
311
311
int ret ;
312
- wchar_t wpath [MAX_PATH ];
313
- if (xutftowcs_path (wpath , path ) < 0 )
312
+ wchar_t wpath [MAX_LONG_PATH ];
313
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
314
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
315
+ core_long_paths ) < 0 )
314
316
return -1 ;
317
+
315
318
ret = _wmkdir (wpath );
316
319
if (!ret && hide_dotfiles == HIDE_DOTFILES_TRUE ) {
317
320
/*
@@ -331,7 +334,7 @@ int mingw_open (const char *filename, int oflags, ...)
331
334
va_list args ;
332
335
unsigned mode ;
333
336
int fd ;
334
- wchar_t wfilename [MAX_PATH ];
337
+ wchar_t wfilename [MAX_LONG_PATH ];
335
338
336
339
va_start (args , oflags );
337
340
mode = va_arg (args , int );
@@ -340,7 +343,7 @@ int mingw_open (const char *filename, int oflags, ...)
340
343
if (filename && !strcmp (filename , "/dev/null" ))
341
344
filename = "nul" ;
342
345
343
- if (xutftowcs_path (wfilename , filename ) < 0 )
346
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
344
347
return -1 ;
345
348
fd = _wopen (wfilename , oflags , mode );
346
349
@@ -393,13 +396,13 @@ FILE *mingw_fopen (const char *filename, const char *otype)
393
396
{
394
397
int hide = 0 ;
395
398
FILE * file ;
396
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
399
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
397
400
if (hide_dotfiles == HIDE_DOTFILES_TRUE &&
398
401
basename ((char * )filename )[0 ] == '.' )
399
402
hide = access (filename , F_OK );
400
403
if (filename && !strcmp (filename , "/dev/null" ))
401
404
filename = "nul" ;
402
- if (xutftowcs_path (wfilename , filename ) < 0 ||
405
+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
403
406
xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
404
407
return NULL ;
405
408
file = _wfopen (wfilename , wotype );
@@ -412,13 +415,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
412
415
{
413
416
int hide = 0 ;
414
417
FILE * file ;
415
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
418
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
416
419
if (hide_dotfiles == HIDE_DOTFILES_TRUE &&
417
420
basename ((char * )filename )[0 ] == '.' )
418
421
hide = access (filename , F_OK );
419
422
if (filename && !strcmp (filename , "/dev/null" ))
420
423
filename = "nul" ;
421
- if (xutftowcs_path (wfilename , filename ) < 0 ||
424
+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
422
425
xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
423
426
return NULL ;
424
427
file = _wfreopen (wfilename , wotype , stream );
@@ -451,25 +454,32 @@ int mingw_fflush(FILE *stream)
451
454
452
455
int mingw_access (const char * filename , int mode )
453
456
{
454
- wchar_t wfilename [MAX_PATH ];
455
- if (xutftowcs_path (wfilename , filename ) < 0 )
457
+ wchar_t wfilename [MAX_LONG_PATH ];
458
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
456
459
return -1 ;
457
460
/* X_OK is not supported by the MSVCRT version */
458
461
return _waccess (wfilename , mode & ~X_OK );
459
462
}
460
463
464
+ /* cached length of current directory for handle_long_path */
465
+ static int current_directory_len = 0 ;
466
+
461
467
int mingw_chdir (const char * dirname )
462
468
{
469
+ int result ;
463
470
wchar_t wdirname [MAX_PATH ];
471
+ /* SetCurrentDirectoryW doesn't support long paths */
464
472
if (xutftowcs_path (wdirname , dirname ) < 0 )
465
473
return -1 ;
466
- return _wchdir (wdirname );
474
+ result = _wchdir (wdirname );
475
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
476
+ return result ;
467
477
}
468
478
469
479
int mingw_chmod (const char * filename , int mode )
470
480
{
471
- wchar_t wfilename [MAX_PATH ];
472
- if (xutftowcs_path (wfilename , filename ) < 0 )
481
+ wchar_t wfilename [MAX_LONG_PATH ];
482
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
473
483
return -1 ;
474
484
return _wchmod (wfilename , mode );
475
485
}
@@ -484,8 +494,8 @@ int mingw_chmod(const char *filename, int mode)
484
494
static int do_lstat (int follow , const char * file_name , struct stat * buf )
485
495
{
486
496
WIN32_FILE_ATTRIBUTE_DATA fdata ;
487
- wchar_t wfilename [MAX_PATH ];
488
- if (xutftowcs_path (wfilename , file_name ) < 0 )
497
+ wchar_t wfilename [MAX_LONG_PATH ];
498
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
489
499
return -1 ;
490
500
491
501
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -550,7 +560,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
550
560
static int do_stat_internal (int follow , const char * file_name , struct stat * buf )
551
561
{
552
562
int namelen ;
553
- char alt_name [PATH_MAX ];
563
+ char alt_name [MAX_LONG_PATH ];
554
564
555
565
if (!do_lstat (follow , file_name , buf ))
556
566
return 0 ;
@@ -566,7 +576,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
566
576
return -1 ;
567
577
while (namelen && file_name [namelen - 1 ] == '/' )
568
578
-- namelen ;
569
- if (!namelen || namelen >= PATH_MAX )
579
+ if (!namelen || namelen >= MAX_LONG_PATH )
570
580
return -1 ;
571
581
572
582
memcpy (alt_name , file_name , namelen );
@@ -628,8 +638,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
628
638
FILETIME mft , aft ;
629
639
int fh , rc ;
630
640
DWORD attrs ;
631
- wchar_t wfilename [MAX_PATH ];
632
- if (xutftowcs_path (wfilename , file_name ) < 0 )
641
+ wchar_t wfilename [MAX_LONG_PATH ];
642
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
633
643
return -1 ;
634
644
635
645
/* must have write permission */
@@ -677,6 +687,7 @@ unsigned int sleep (unsigned int seconds)
677
687
char * mingw_mktemp (char * template )
678
688
{
679
689
wchar_t wtemplate [MAX_PATH ];
690
+ /* we need to return the path, thus no long paths here! */
680
691
if (xutftowcs_path (wtemplate , template ) < 0 )
681
692
return NULL ;
682
693
if (!_wmktemp (wtemplate ))
@@ -1037,6 +1048,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1037
1048
si .hStdOutput = winansi_get_osfhandle (fhout );
1038
1049
si .hStdError = winansi_get_osfhandle (fherr );
1039
1050
1051
+ /* executables and the current directory don't support long paths */
1040
1052
if (xutftowcs_path (wcmd , cmd ) < 0 )
1041
1053
return -1 ;
1042
1054
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -1612,8 +1624,9 @@ int mingw_rename(const char *pold, const char *pnew)
1612
1624
{
1613
1625
DWORD attrs , gle ;
1614
1626
int tries = 0 ;
1615
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
1616
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
1627
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
1628
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
1629
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
1617
1630
return -1 ;
1618
1631
1619
1632
/*
@@ -1889,9 +1902,9 @@ int link(const char *oldpath, const char *newpath)
1889
1902
{
1890
1903
typedef BOOL (WINAPI * T )(LPCWSTR , LPCWSTR , LPSECURITY_ATTRIBUTES );
1891
1904
static T create_hard_link = NULL ;
1892
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
1893
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
1894
- xutftowcs_path (wnewpath , newpath ) < 0 )
1905
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
1906
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
1907
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
1895
1908
return -1 ;
1896
1909
1897
1910
if (!create_hard_link ) {
@@ -2072,6 +2085,68 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
2072
2085
return -1 ;
2073
2086
}
2074
2087
2088
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
2089
+ {
2090
+ int result ;
2091
+ wchar_t buf [MAX_LONG_PATH ];
2092
+
2093
+ /*
2094
+ * we don't need special handling if path is relative to the current
2095
+ * directory, and current directory + path don't exceed the desired
2096
+ * max_path limit. This should cover > 99 % of cases with minimal
2097
+ * performance impact (git almost always uses relative paths).
2098
+ */
2099
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
2100
+ (current_directory_len + len < max_path ))
2101
+ return len ;
2102
+
2103
+ /*
2104
+ * handle everything else:
2105
+ * - absolute paths: "C:\dir\file"
2106
+ * - absolute UNC paths: "\\server\share\dir\file"
2107
+ * - absolute paths on current drive: "\dir\file"
2108
+ * - relative paths on other drive: "X:file"
2109
+ * - prefixed paths: "\\?\...", "\\.\..."
2110
+ */
2111
+
2112
+ /* convert to absolute path using GetFullPathNameW */
2113
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
2114
+ if (!result ) {
2115
+ errno = err_win_to_posix (GetLastError ());
2116
+ return -1 ;
2117
+ }
2118
+
2119
+ /*
2120
+ * return absolute path if it fits within max_path (even if
2121
+ * "cwd + path" doesn't due to '..' components)
2122
+ */
2123
+ if (result < max_path ) {
2124
+ wcscpy (path , buf );
2125
+ return result ;
2126
+ }
2127
+
2128
+ /* error out if we shouldn't expand the path or buf is too small */
2129
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
2130
+ errno = ENAMETOOLONG ;
2131
+ return -1 ;
2132
+ }
2133
+
2134
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
2135
+ if (buf [0 ] == '\\' ) {
2136
+ /* ...unless already prefixed */
2137
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
2138
+ return len ;
2139
+
2140
+ wcscpy (path , L"\\\\?\\UNC\\" );
2141
+ wcscpy (path + 8 , buf + 2 );
2142
+ return result + 6 ;
2143
+ } else {
2144
+ wcscpy (path , L"\\\\?\\" );
2145
+ wcscpy (path + 4 , buf );
2146
+ return result + 4 ;
2147
+ }
2148
+ }
2149
+
2075
2150
/*
2076
2151
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
2077
2152
* mingw startup code, see init.c in mingw runtime).
@@ -2175,4 +2250,7 @@ void mingw_startup()
2175
2250
2176
2251
/* initialize Unicode console */
2177
2252
winansi_init ();
2253
+
2254
+ /* init length of current directory for handle_long_path */
2255
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
2178
2256
}
0 commit comments