@@ -206,8 +206,8 @@ static int ask_yes_no_if_possible(const char *format, ...)
206
206
int mingw_unlink (const char * pathname )
207
207
{
208
208
int ret , tries = 0 ;
209
- wchar_t wpathname [MAX_PATH ];
210
- if (xutftowcs_path (wpathname , pathname ) < 0 )
209
+ wchar_t wpathname [MAX_LONG_PATH ];
210
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
211
211
return -1 ;
212
212
213
213
/* read-only files cannot be removed */
@@ -236,7 +236,7 @@ static int is_dir_empty(const wchar_t *wpath)
236
236
{
237
237
WIN32_FIND_DATAW findbuf ;
238
238
HANDLE handle ;
239
- wchar_t wbuf [MAX_PATH + 2 ];
239
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
240
240
wcscpy (wbuf , wpath );
241
241
wcscat (wbuf , L"\\*" );
242
242
handle = FindFirstFileW (wbuf , & findbuf );
@@ -257,8 +257,8 @@ static int is_dir_empty(const wchar_t *wpath)
257
257
int mingw_rmdir (const char * pathname )
258
258
{
259
259
int ret , tries = 0 ;
260
- wchar_t wpathname [MAX_PATH ];
261
- if (xutftowcs_path (wpathname , pathname ) < 0 )
260
+ wchar_t wpathname [MAX_LONG_PATH ];
261
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
262
262
return -1 ;
263
263
264
264
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -298,9 +298,9 @@ static int make_hidden(const wchar_t *path)
298
298
299
299
void mingw_mark_as_git_dir (const char * dir )
300
300
{
301
- wchar_t wdir [MAX_PATH ];
301
+ wchar_t wdir [MAX_LONG_PATH ];
302
302
if (hide_dotfiles != HIDE_DOTFILES_FALSE && !is_bare_repository ())
303
- if (xutftowcs_path (wdir , dir ) < 0 || make_hidden (wdir ))
303
+ if (xutftowcs_long_path (wdir , dir ) < 0 || make_hidden (wdir ))
304
304
warning ("Failed to make '%s' hidden" , dir );
305
305
git_config_set ("core.hideDotFiles" ,
306
306
hide_dotfiles == HIDE_DOTFILES_FALSE ? "false" :
@@ -311,9 +311,12 @@ void mingw_mark_as_git_dir(const char *dir)
311
311
int mingw_mkdir (const char * path , int mode )
312
312
{
313
313
int ret ;
314
- wchar_t wpath [MAX_PATH ];
315
- if (xutftowcs_path (wpath , path ) < 0 )
314
+ wchar_t wpath [MAX_LONG_PATH ];
315
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
316
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
317
+ core_long_paths ) < 0 )
316
318
return -1 ;
319
+
317
320
ret = _wmkdir (wpath );
318
321
if (!ret && hide_dotfiles == HIDE_DOTFILES_TRUE ) {
319
322
/*
@@ -333,7 +336,7 @@ int mingw_open (const char *filename, int oflags, ...)
333
336
va_list args ;
334
337
unsigned mode ;
335
338
int fd ;
336
- wchar_t wfilename [MAX_PATH ];
339
+ wchar_t wfilename [MAX_LONG_PATH ];
337
340
338
341
va_start (args , oflags );
339
342
mode = va_arg (args , int );
@@ -342,7 +345,7 @@ int mingw_open (const char *filename, int oflags, ...)
342
345
if (filename && !strcmp (filename , "/dev/null" ))
343
346
filename = "nul" ;
344
347
345
- if (xutftowcs_path (wfilename , filename ) < 0 )
348
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
346
349
return -1 ;
347
350
fd = _wopen (wfilename , oflags , mode );
348
351
@@ -395,13 +398,13 @@ FILE *mingw_fopen (const char *filename, const char *otype)
395
398
{
396
399
int hide = 0 ;
397
400
FILE * file ;
398
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
401
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
399
402
if (hide_dotfiles == HIDE_DOTFILES_TRUE &&
400
403
basename ((char * )filename )[0 ] == '.' )
401
404
hide = access (filename , F_OK );
402
405
if (filename && !strcmp (filename , "/dev/null" ))
403
406
filename = "nul" ;
404
- if (xutftowcs_path (wfilename , filename ) < 0 ||
407
+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
405
408
xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
406
409
return NULL ;
407
410
file = _wfopen (wfilename , wotype );
@@ -414,13 +417,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
414
417
{
415
418
int hide = 0 ;
416
419
FILE * file ;
417
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
420
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
418
421
if (hide_dotfiles == HIDE_DOTFILES_TRUE &&
419
422
basename ((char * )filename )[0 ] == '.' )
420
423
hide = access (filename , F_OK );
421
424
if (filename && !strcmp (filename , "/dev/null" ))
422
425
filename = "nul" ;
423
- if (xutftowcs_path (wfilename , filename ) < 0 ||
426
+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
424
427
xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
425
428
return NULL ;
426
429
file = _wfreopen (wfilename , wotype , stream );
@@ -470,25 +473,32 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
470
473
471
474
int mingw_access (const char * filename , int mode )
472
475
{
473
- wchar_t wfilename [MAX_PATH ];
474
- if (xutftowcs_path (wfilename , filename ) < 0 )
476
+ wchar_t wfilename [MAX_LONG_PATH ];
477
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
475
478
return -1 ;
476
479
/* X_OK is not supported by the MSVCRT version */
477
480
return _waccess (wfilename , mode & ~X_OK );
478
481
}
479
482
483
+ /* cached length of current directory for handle_long_path */
484
+ static int current_directory_len = 0 ;
485
+
480
486
int mingw_chdir (const char * dirname )
481
487
{
488
+ int result ;
482
489
wchar_t wdirname [MAX_PATH ];
490
+ /* SetCurrentDirectoryW doesn't support long paths */
483
491
if (xutftowcs_path (wdirname , dirname ) < 0 )
484
492
return -1 ;
485
- return _wchdir (wdirname );
493
+ result = _wchdir (wdirname );
494
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
495
+ return result ;
486
496
}
487
497
488
498
int mingw_chmod (const char * filename , int mode )
489
499
{
490
- wchar_t wfilename [MAX_PATH ];
491
- if (xutftowcs_path (wfilename , filename ) < 0 )
500
+ wchar_t wfilename [MAX_LONG_PATH ];
501
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
492
502
return -1 ;
493
503
return _wchmod (wfilename , mode );
494
504
}
@@ -536,8 +546,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
536
546
static int do_lstat (int follow , const char * file_name , struct stat * buf )
537
547
{
538
548
WIN32_FILE_ATTRIBUTE_DATA fdata ;
539
- wchar_t wfilename [MAX_PATH ];
540
- if (xutftowcs_path (wfilename , file_name ) < 0 )
549
+ wchar_t wfilename [MAX_LONG_PATH ];
550
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
541
551
return -1 ;
542
552
543
553
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -608,7 +618,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
608
618
static int do_stat_internal (int follow , const char * file_name , struct stat * buf )
609
619
{
610
620
int namelen ;
611
- char alt_name [PATH_MAX ];
621
+ char alt_name [MAX_LONG_PATH ];
612
622
613
623
if (!do_lstat (follow , file_name , buf ))
614
624
return 0 ;
@@ -624,7 +634,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
624
634
return -1 ;
625
635
while (namelen && file_name [namelen - 1 ] == '/' )
626
636
-- namelen ;
627
- if (!namelen || namelen >= PATH_MAX )
637
+ if (!namelen || namelen >= MAX_LONG_PATH )
628
638
return -1 ;
629
639
630
640
memcpy (alt_name , file_name , namelen );
@@ -686,8 +696,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
686
696
FILETIME mft , aft ;
687
697
int fh , rc ;
688
698
DWORD attrs ;
689
- wchar_t wfilename [MAX_PATH ];
690
- if (xutftowcs_path (wfilename , file_name ) < 0 )
699
+ wchar_t wfilename [MAX_LONG_PATH ];
700
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
691
701
return -1 ;
692
702
693
703
/* must have write permission */
@@ -735,6 +745,7 @@ unsigned int sleep (unsigned int seconds)
735
745
char * mingw_mktemp (char * template )
736
746
{
737
747
wchar_t wtemplate [MAX_PATH ];
748
+ /* we need to return the path, thus no long paths here! */
738
749
if (xutftowcs_path (wtemplate , template ) < 0 )
739
750
return NULL ;
740
751
if (!_wmktemp (wtemplate ))
@@ -1088,6 +1099,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1088
1099
si .hStdOutput = winansi_get_osfhandle (fhout );
1089
1100
si .hStdError = winansi_get_osfhandle (fherr );
1090
1101
1102
+ /* executables and the current directory don't support long paths */
1091
1103
if (xutftowcs_path (wcmd , cmd ) < 0 )
1092
1104
return -1 ;
1093
1105
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -1663,8 +1675,9 @@ int mingw_rename(const char *pold, const char *pnew)
1663
1675
{
1664
1676
DWORD attrs , gle ;
1665
1677
int tries = 0 ;
1666
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
1667
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
1678
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
1679
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
1680
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
1668
1681
return -1 ;
1669
1682
1670
1683
/*
@@ -1946,9 +1959,9 @@ int link(const char *oldpath, const char *newpath)
1946
1959
{
1947
1960
typedef BOOL (WINAPI * T )(LPCWSTR , LPCWSTR , LPSECURITY_ATTRIBUTES );
1948
1961
static T create_hard_link = NULL ;
1949
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
1950
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
1951
- xutftowcs_path (wnewpath , newpath ) < 0 )
1962
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
1963
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
1964
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
1952
1965
return -1 ;
1953
1966
1954
1967
if (!create_hard_link ) {
@@ -2160,6 +2173,68 @@ static void setup_windows_environment()
2160
2173
setenv ("TERM" , "cygwin" , 1 );
2161
2174
}
2162
2175
2176
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
2177
+ {
2178
+ int result ;
2179
+ wchar_t buf [MAX_LONG_PATH ];
2180
+
2181
+ /*
2182
+ * we don't need special handling if path is relative to the current
2183
+ * directory, and current directory + path don't exceed the desired
2184
+ * max_path limit. This should cover > 99 % of cases with minimal
2185
+ * performance impact (git almost always uses relative paths).
2186
+ */
2187
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
2188
+ (current_directory_len + len < max_path ))
2189
+ return len ;
2190
+
2191
+ /*
2192
+ * handle everything else:
2193
+ * - absolute paths: "C:\dir\file"
2194
+ * - absolute UNC paths: "\\server\share\dir\file"
2195
+ * - absolute paths on current drive: "\dir\file"
2196
+ * - relative paths on other drive: "X:file"
2197
+ * - prefixed paths: "\\?\...", "\\.\..."
2198
+ */
2199
+
2200
+ /* convert to absolute path using GetFullPathNameW */
2201
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
2202
+ if (!result ) {
2203
+ errno = err_win_to_posix (GetLastError ());
2204
+ return -1 ;
2205
+ }
2206
+
2207
+ /*
2208
+ * return absolute path if it fits within max_path (even if
2209
+ * "cwd + path" doesn't due to '..' components)
2210
+ */
2211
+ if (result < max_path ) {
2212
+ wcscpy (path , buf );
2213
+ return result ;
2214
+ }
2215
+
2216
+ /* error out if we shouldn't expand the path or buf is too small */
2217
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
2218
+ errno = ENAMETOOLONG ;
2219
+ return -1 ;
2220
+ }
2221
+
2222
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
2223
+ if (buf [0 ] == '\\' ) {
2224
+ /* ...unless already prefixed */
2225
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
2226
+ return len ;
2227
+
2228
+ wcscpy (path , L"\\\\?\\UNC\\" );
2229
+ wcscpy (path + 8 , buf + 2 );
2230
+ return result + 6 ;
2231
+ } else {
2232
+ wcscpy (path , L"\\\\?\\" );
2233
+ wcscpy (path + 4 , buf );
2234
+ return result + 4 ;
2235
+ }
2236
+ }
2237
+
2163
2238
/*
2164
2239
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
2165
2240
* mingw startup code, see init.c in mingw runtime).
@@ -2251,6 +2326,9 @@ void mingw_startup()
2251
2326
2252
2327
/* initialize Unicode console */
2253
2328
winansi_init ();
2329
+
2330
+ /* init length of current directory for handle_long_path */
2331
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
2254
2332
}
2255
2333
2256
2334
int uname (struct utsname * buf )
0 commit comments