@@ -208,8 +208,8 @@ static int ask_yes_no_if_possible(const char *format, ...)
208
208
int mingw_unlink (const char * pathname )
209
209
{
210
210
int ret , tries = 0 ;
211
- wchar_t wpathname [MAX_PATH ];
212
- if (xutftowcs_path (wpathname , pathname ) < 0 )
211
+ wchar_t wpathname [MAX_LONG_PATH ];
212
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
213
213
return -1 ;
214
214
215
215
/* read-only files cannot be removed */
@@ -238,7 +238,7 @@ static int is_dir_empty(const wchar_t *wpath)
238
238
{
239
239
WIN32_FIND_DATAW findbuf ;
240
240
HANDLE handle ;
241
- wchar_t wbuf [MAX_PATH + 2 ];
241
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
242
242
wcscpy (wbuf , wpath );
243
243
wcscat (wbuf , L"\\*" );
244
244
handle = FindFirstFileW (wbuf , & findbuf );
@@ -259,8 +259,8 @@ static int is_dir_empty(const wchar_t *wpath)
259
259
int mingw_rmdir (const char * pathname )
260
260
{
261
261
int ret , tries = 0 ;
262
- wchar_t wpathname [MAX_PATH ];
263
- if (xutftowcs_path (wpathname , pathname ) < 0 )
262
+ wchar_t wpathname [MAX_LONG_PATH ];
263
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
264
264
return -1 ;
265
265
266
266
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -300,9 +300,9 @@ static int make_hidden(const wchar_t *path)
300
300
301
301
void mingw_mark_as_git_dir (const char * dir )
302
302
{
303
- wchar_t wdir [MAX_PATH ];
303
+ wchar_t wdir [MAX_LONG_PATH ];
304
304
if (hide_dotfiles != HIDE_DOTFILES_FALSE && !is_bare_repository ())
305
- if (xutftowcs_path (wdir , dir ) < 0 || make_hidden (wdir ))
305
+ if (xutftowcs_long_path (wdir , dir ) < 0 || make_hidden (wdir ))
306
306
warning ("Failed to make '%s' hidden" , dir );
307
307
git_config_set ("core.hideDotFiles" ,
308
308
hide_dotfiles == HIDE_DOTFILES_FALSE ? "false" :
@@ -313,9 +313,12 @@ void mingw_mark_as_git_dir(const char *dir)
313
313
int mingw_mkdir (const char * path , int mode )
314
314
{
315
315
int ret ;
316
- wchar_t wpath [MAX_PATH ];
317
- if (xutftowcs_path (wpath , path ) < 0 )
316
+ wchar_t wpath [MAX_LONG_PATH ];
317
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
318
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
319
+ core_long_paths ) < 0 )
318
320
return -1 ;
321
+
319
322
ret = _wmkdir (wpath );
320
323
if (!ret && hide_dotfiles == HIDE_DOTFILES_TRUE ) {
321
324
/*
@@ -335,7 +338,7 @@ int mingw_open (const char *filename, int oflags, ...)
335
338
va_list args ;
336
339
unsigned mode ;
337
340
int fd ;
338
- wchar_t wfilename [MAX_PATH ];
341
+ wchar_t wfilename [MAX_LONG_PATH ];
339
342
340
343
va_start (args , oflags );
341
344
mode = va_arg (args , int );
@@ -344,7 +347,7 @@ int mingw_open (const char *filename, int oflags, ...)
344
347
if (filename && !strcmp (filename , "/dev/null" ))
345
348
filename = "nul" ;
346
349
347
- if (xutftowcs_path (wfilename , filename ) < 0 )
350
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
348
351
return -1 ;
349
352
fd = _wopen (wfilename , oflags , mode );
350
353
@@ -397,13 +400,13 @@ FILE *mingw_fopen (const char *filename, const char *otype)
397
400
{
398
401
int hide = 0 ;
399
402
FILE * file ;
400
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
403
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
401
404
if (hide_dotfiles == HIDE_DOTFILES_TRUE &&
402
405
basename ((char * )filename )[0 ] == '.' )
403
406
hide = access (filename , F_OK );
404
407
if (filename && !strcmp (filename , "/dev/null" ))
405
408
filename = "nul" ;
406
- if (xutftowcs_path (wfilename , filename ) < 0 ||
409
+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
407
410
xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
408
411
return NULL ;
409
412
file = _wfopen (wfilename , wotype );
@@ -416,13 +419,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
416
419
{
417
420
int hide = 0 ;
418
421
FILE * file ;
419
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
422
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
420
423
if (hide_dotfiles == HIDE_DOTFILES_TRUE &&
421
424
basename ((char * )filename )[0 ] == '.' )
422
425
hide = access (filename , F_OK );
423
426
if (filename && !strcmp (filename , "/dev/null" ))
424
427
filename = "nul" ;
425
- if (xutftowcs_path (wfilename , filename ) < 0 ||
428
+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
426
429
xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
427
430
return NULL ;
428
431
file = _wfreopen (wfilename , wotype , stream );
@@ -455,25 +458,32 @@ int mingw_fflush(FILE *stream)
455
458
456
459
int mingw_access (const char * filename , int mode )
457
460
{
458
- wchar_t wfilename [MAX_PATH ];
459
- if (xutftowcs_path (wfilename , filename ) < 0 )
461
+ wchar_t wfilename [MAX_LONG_PATH ];
462
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
460
463
return -1 ;
461
464
/* X_OK is not supported by the MSVCRT version */
462
465
return _waccess (wfilename , mode & ~X_OK );
463
466
}
464
467
468
+ /* cached length of current directory for handle_long_path */
469
+ static int current_directory_len = 0 ;
470
+
465
471
int mingw_chdir (const char * dirname )
466
472
{
473
+ int result ;
467
474
wchar_t wdirname [MAX_PATH ];
475
+ /* SetCurrentDirectoryW doesn't support long paths */
468
476
if (xutftowcs_path (wdirname , dirname ) < 0 )
469
477
return -1 ;
470
- return _wchdir (wdirname );
478
+ result = _wchdir (wdirname );
479
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
480
+ return result ;
471
481
}
472
482
473
483
int mingw_chmod (const char * filename , int mode )
474
484
{
475
- wchar_t wfilename [MAX_PATH ];
476
- if (xutftowcs_path (wfilename , filename ) < 0 )
485
+ wchar_t wfilename [MAX_LONG_PATH ];
486
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
477
487
return -1 ;
478
488
return _wchmod (wfilename , mode );
479
489
}
@@ -488,8 +498,8 @@ int mingw_chmod(const char *filename, int mode)
488
498
static int do_lstat (int follow , const char * file_name , struct stat * buf )
489
499
{
490
500
WIN32_FILE_ATTRIBUTE_DATA fdata ;
491
- wchar_t wfilename [MAX_PATH ];
492
- if (xutftowcs_path (wfilename , file_name ) < 0 )
501
+ wchar_t wfilename [MAX_LONG_PATH ];
502
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
493
503
return -1 ;
494
504
495
505
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -554,7 +564,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
554
564
static int do_stat_internal (int follow , const char * file_name , struct stat * buf )
555
565
{
556
566
int namelen ;
557
- char alt_name [PATH_MAX ];
567
+ char alt_name [MAX_LONG_PATH ];
558
568
559
569
if (!do_lstat (follow , file_name , buf ))
560
570
return 0 ;
@@ -570,7 +580,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
570
580
return -1 ;
571
581
while (namelen && file_name [namelen - 1 ] == '/' )
572
582
-- namelen ;
573
- if (!namelen || namelen >= PATH_MAX )
583
+ if (!namelen || namelen >= MAX_LONG_PATH )
574
584
return -1 ;
575
585
576
586
memcpy (alt_name , file_name , namelen );
@@ -632,8 +642,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
632
642
FILETIME mft , aft ;
633
643
int fh , rc ;
634
644
DWORD attrs ;
635
- wchar_t wfilename [MAX_PATH ];
636
- if (xutftowcs_path (wfilename , file_name ) < 0 )
645
+ wchar_t wfilename [MAX_LONG_PATH ];
646
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
637
647
return -1 ;
638
648
639
649
/* must have write permission */
@@ -681,6 +691,7 @@ unsigned int sleep (unsigned int seconds)
681
691
char * mingw_mktemp (char * template )
682
692
{
683
693
wchar_t wtemplate [MAX_PATH ];
694
+ /* we need to return the path, thus no long paths here! */
684
695
if (xutftowcs_path (wtemplate , template ) < 0 )
685
696
return NULL ;
686
697
if (!_wmktemp (wtemplate ))
@@ -1048,6 +1059,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1048
1059
si .hStdOutput = winansi_get_osfhandle (fhout );
1049
1060
si .hStdError = winansi_get_osfhandle (fherr );
1050
1061
1062
+ /* executables and the current directory don't support long paths */
1051
1063
if (xutftowcs_path (wcmd , cmd ) < 0 )
1052
1064
return -1 ;
1053
1065
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -1677,8 +1689,9 @@ int mingw_rename(const char *pold, const char *pnew)
1677
1689
{
1678
1690
DWORD attrs , gle ;
1679
1691
int tries = 0 ;
1680
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
1681
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
1692
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
1693
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
1694
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
1682
1695
return -1 ;
1683
1696
1684
1697
/*
@@ -1960,9 +1973,9 @@ int link(const char *oldpath, const char *newpath)
1960
1973
{
1961
1974
typedef BOOL (WINAPI * T )(LPCWSTR , LPCWSTR , LPSECURITY_ATTRIBUTES );
1962
1975
static T create_hard_link = NULL ;
1963
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
1964
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
1965
- xutftowcs_path (wnewpath , newpath ) < 0 )
1976
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
1977
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
1978
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
1966
1979
return -1 ;
1967
1980
1968
1981
if (!create_hard_link ) {
@@ -2143,6 +2156,68 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
2143
2156
return -1 ;
2144
2157
}
2145
2158
2159
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
2160
+ {
2161
+ int result ;
2162
+ wchar_t buf [MAX_LONG_PATH ];
2163
+
2164
+ /*
2165
+ * we don't need special handling if path is relative to the current
2166
+ * directory, and current directory + path don't exceed the desired
2167
+ * max_path limit. This should cover > 99 % of cases with minimal
2168
+ * performance impact (git almost always uses relative paths).
2169
+ */
2170
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
2171
+ (current_directory_len + len < max_path ))
2172
+ return len ;
2173
+
2174
+ /*
2175
+ * handle everything else:
2176
+ * - absolute paths: "C:\dir\file"
2177
+ * - absolute UNC paths: "\\server\share\dir\file"
2178
+ * - absolute paths on current drive: "\dir\file"
2179
+ * - relative paths on other drive: "X:file"
2180
+ * - prefixed paths: "\\?\...", "\\.\..."
2181
+ */
2182
+
2183
+ /* convert to absolute path using GetFullPathNameW */
2184
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
2185
+ if (!result ) {
2186
+ errno = err_win_to_posix (GetLastError ());
2187
+ return -1 ;
2188
+ }
2189
+
2190
+ /*
2191
+ * return absolute path if it fits within max_path (even if
2192
+ * "cwd + path" doesn't due to '..' components)
2193
+ */
2194
+ if (result < max_path ) {
2195
+ wcscpy (path , buf );
2196
+ return result ;
2197
+ }
2198
+
2199
+ /* error out if we shouldn't expand the path or buf is too small */
2200
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
2201
+ errno = ENAMETOOLONG ;
2202
+ return -1 ;
2203
+ }
2204
+
2205
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
2206
+ if (buf [0 ] == '\\' ) {
2207
+ /* ...unless already prefixed */
2208
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
2209
+ return len ;
2210
+
2211
+ wcscpy (path , L"\\\\?\\UNC\\" );
2212
+ wcscpy (path + 8 , buf + 2 );
2213
+ return result + 6 ;
2214
+ } else {
2215
+ wcscpy (path , L"\\\\?\\" );
2216
+ wcscpy (path + 4 , buf );
2217
+ return result + 4 ;
2218
+ }
2219
+ }
2220
+
2146
2221
/*
2147
2222
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
2148
2223
* mingw startup code, see init.c in mingw runtime).
@@ -2264,6 +2339,9 @@ void mingw_startup()
2264
2339
2265
2340
/* initialize Unicode console */
2266
2341
winansi_init ();
2342
+
2343
+ /* init length of current directory for handle_long_path */
2344
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
2267
2345
}
2268
2346
2269
2347
int mingw_isatty (int fd ) {
0 commit comments