@@ -205,8 +205,8 @@ static int ask_yes_no_if_possible(const char *format, ...)
205
205
int mingw_unlink (const char * pathname )
206
206
{
207
207
int ret , tries = 0 ;
208
- wchar_t wpathname [MAX_PATH ];
209
- if (xutftowcs_path (wpathname , pathname ) < 0 )
208
+ wchar_t wpathname [MAX_LONG_PATH ];
209
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
210
210
return -1 ;
211
211
212
212
/* read-only files cannot be removed */
@@ -235,7 +235,7 @@ static int is_dir_empty(const wchar_t *wpath)
235
235
{
236
236
WIN32_FIND_DATAW findbuf ;
237
237
HANDLE handle ;
238
- wchar_t wbuf [MAX_PATH + 2 ];
238
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
239
239
wcscpy (wbuf , wpath );
240
240
wcscat (wbuf , L"\\*" );
241
241
handle = FindFirstFileW (wbuf , & findbuf );
@@ -256,8 +256,8 @@ static int is_dir_empty(const wchar_t *wpath)
256
256
int mingw_rmdir (const char * pathname )
257
257
{
258
258
int ret , tries = 0 ;
259
- wchar_t wpathname [MAX_PATH ];
260
- if (xutftowcs_path (wpathname , pathname ) < 0 )
259
+ wchar_t wpathname [MAX_LONG_PATH ];
260
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
261
261
return -1 ;
262
262
263
263
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -332,9 +332,12 @@ static int set_hidden_flag(const wchar_t *path, int set)
332
332
int mingw_mkdir (const char * path , int mode )
333
333
{
334
334
int ret ;
335
- wchar_t wpath [MAX_PATH ];
336
- if (xutftowcs_path (wpath , path ) < 0 )
335
+ wchar_t wpath [MAX_LONG_PATH ];
336
+ /* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
337
+ if (xutftowcs_path_ex (wpath , path , MAX_LONG_PATH , -1 , 248 ,
338
+ core_long_paths ) < 0 )
337
339
return -1 ;
340
+
338
341
ret = _wmkdir (wpath );
339
342
if (!ret && needs_hiding (path ))
340
343
return set_hidden_flag (wpath , 1 );
@@ -346,7 +349,7 @@ int mingw_open (const char *filename, int oflags, ...)
346
349
va_list args ;
347
350
unsigned mode ;
348
351
int fd ;
349
- wchar_t wfilename [MAX_PATH ];
352
+ wchar_t wfilename [MAX_LONG_PATH ];
350
353
351
354
va_start (args , oflags );
352
355
mode = va_arg (args , int );
@@ -355,7 +358,7 @@ int mingw_open (const char *filename, int oflags, ...)
355
358
if (filename && !strcmp (filename , "/dev/null" ))
356
359
filename = "nul" ;
357
360
358
- if (xutftowcs_path (wfilename , filename ) < 0 )
361
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
359
362
return -1 ;
360
363
fd = _wopen (wfilename , oflags , mode );
361
364
@@ -412,10 +415,10 @@ FILE *mingw_fopen (const char *filename, const char *otype)
412
415
{
413
416
int hide = needs_hiding (filename );
414
417
FILE * file ;
415
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
418
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
416
419
if (filename && !strcmp (filename , "/dev/null" ))
417
420
filename = "nul" ;
418
- if (xutftowcs_path (wfilename , filename ) < 0 ||
421
+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
419
422
xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
420
423
return NULL ;
421
424
if (hide && !access (filename , F_OK ) && set_hidden_flag (wfilename , 0 )) {
@@ -432,10 +435,10 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
432
435
{
433
436
int hide = needs_hiding (filename );
434
437
FILE * file ;
435
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
438
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
436
439
if (filename && !strcmp (filename , "/dev/null" ))
437
440
filename = "nul" ;
438
- if (xutftowcs_path (wfilename , filename ) < 0 ||
441
+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
439
442
xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
440
443
return NULL ;
441
444
if (hide && !access (filename , F_OK ) && set_hidden_flag (wfilename , 0 )) {
@@ -489,25 +492,31 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
489
492
490
493
int mingw_access (const char * filename , int mode )
491
494
{
492
- wchar_t wfilename [MAX_PATH ];
493
- if (xutftowcs_path (wfilename , filename ) < 0 )
495
+ wchar_t wfilename [MAX_LONG_PATH ];
496
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
494
497
return -1 ;
495
498
/* X_OK is not supported by the MSVCRT version */
496
499
return _waccess (wfilename , mode & ~X_OK );
497
500
}
498
501
502
+ /* cached length of current directory for handle_long_path */
503
+ static int current_directory_len = 0 ;
504
+
499
505
int mingw_chdir (const char * dirname )
500
506
{
501
- wchar_t wdirname [MAX_PATH ];
502
- if (xutftowcs_path (wdirname , dirname ) < 0 )
507
+ int result ;
508
+ wchar_t wdirname [MAX_LONG_PATH ];
509
+ if (xutftowcs_long_path (wdirname , dirname ) < 0 )
503
510
return -1 ;
504
- return _wchdir (wdirname );
511
+ result = _wchdir (wdirname );
512
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
513
+ return result ;
505
514
}
506
515
507
516
int mingw_chmod (const char * filename , int mode )
508
517
{
509
- wchar_t wfilename [MAX_PATH ];
510
- if (xutftowcs_path (wfilename , filename ) < 0 )
518
+ wchar_t wfilename [MAX_LONG_PATH ];
519
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
511
520
return -1 ;
512
521
return _wchmod (wfilename , mode );
513
522
}
@@ -555,8 +564,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
555
564
static int do_lstat (int follow , const char * file_name , struct stat * buf )
556
565
{
557
566
WIN32_FILE_ATTRIBUTE_DATA fdata ;
558
- wchar_t wfilename [MAX_PATH ];
559
- if (xutftowcs_path (wfilename , file_name ) < 0 )
567
+ wchar_t wfilename [MAX_LONG_PATH ];
568
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
560
569
return -1 ;
561
570
562
571
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -627,7 +636,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
627
636
static int do_stat_internal (int follow , const char * file_name , struct stat * buf )
628
637
{
629
638
int namelen ;
630
- char alt_name [PATH_MAX ];
639
+ char alt_name [MAX_LONG_PATH ];
631
640
632
641
if (!do_lstat (follow , file_name , buf ))
633
642
return 0 ;
@@ -643,7 +652,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
643
652
return -1 ;
644
653
while (namelen && file_name [namelen - 1 ] == '/' )
645
654
-- namelen ;
646
- if (!namelen || namelen >= PATH_MAX )
655
+ if (!namelen || namelen >= MAX_LONG_PATH )
647
656
return -1 ;
648
657
649
658
memcpy (alt_name , file_name , namelen );
@@ -705,8 +714,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
705
714
FILETIME mft , aft ;
706
715
int fh , rc ;
707
716
DWORD attrs ;
708
- wchar_t wfilename [MAX_PATH ];
709
- if (xutftowcs_path (wfilename , file_name ) < 0 )
717
+ wchar_t wfilename [MAX_LONG_PATH ];
718
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
710
719
return -1 ;
711
720
712
721
/* must have write permission */
@@ -754,6 +763,7 @@ unsigned int sleep (unsigned int seconds)
754
763
char * mingw_mktemp (char * template )
755
764
{
756
765
wchar_t wtemplate [MAX_PATH ];
766
+ /* we need to return the path, thus no long paths here! */
757
767
if (xutftowcs_path (wtemplate , template ) < 0 )
758
768
return NULL ;
759
769
if (!_wmktemp (wtemplate ))
@@ -1104,6 +1114,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1104
1114
si .hStdOutput = winansi_get_osfhandle (fhout );
1105
1115
si .hStdError = winansi_get_osfhandle (fherr );
1106
1116
1117
+ /* executables and the current directory don't support long paths */
1107
1118
if (xutftowcs_path (wcmd , cmd ) < 0 )
1108
1119
return -1 ;
1109
1120
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -1679,8 +1690,9 @@ int mingw_rename(const char *pold, const char *pnew)
1679
1690
{
1680
1691
DWORD attrs , gle ;
1681
1692
int tries = 0 ;
1682
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
1683
- if (xutftowcs_path (wpold , pold ) < 0 || xutftowcs_path (wpnew , pnew ) < 0 )
1693
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
1694
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
1695
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
1684
1696
return -1 ;
1685
1697
1686
1698
/*
@@ -1920,9 +1932,9 @@ int link(const char *oldpath, const char *newpath)
1920
1932
{
1921
1933
typedef BOOL (WINAPI * T )(LPCWSTR , LPCWSTR , LPSECURITY_ATTRIBUTES );
1922
1934
static T create_hard_link = NULL ;
1923
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
1924
- if (xutftowcs_path (woldpath , oldpath ) < 0 ||
1925
- xutftowcs_path (wnewpath , newpath ) < 0 )
1935
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
1936
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
1937
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
1926
1938
return -1 ;
1927
1939
1928
1940
if (!create_hard_link ) {
@@ -2135,6 +2147,68 @@ static void setup_windows_environment(void)
2135
2147
setenv ("TERM" , "cygwin" , 1 );
2136
2148
}
2137
2149
2150
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
2151
+ {
2152
+ int result ;
2153
+ wchar_t buf [MAX_LONG_PATH ];
2154
+
2155
+ /*
2156
+ * we don't need special handling if path is relative to the current
2157
+ * directory, and current directory + path don't exceed the desired
2158
+ * max_path limit. This should cover > 99 % of cases with minimal
2159
+ * performance impact (git almost always uses relative paths).
2160
+ */
2161
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
2162
+ (current_directory_len + len < max_path ))
2163
+ return len ;
2164
+
2165
+ /*
2166
+ * handle everything else:
2167
+ * - absolute paths: "C:\dir\file"
2168
+ * - absolute UNC paths: "\\server\share\dir\file"
2169
+ * - absolute paths on current drive: "\dir\file"
2170
+ * - relative paths on other drive: "X:file"
2171
+ * - prefixed paths: "\\?\...", "\\.\..."
2172
+ */
2173
+
2174
+ /* convert to absolute path using GetFullPathNameW */
2175
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
2176
+ if (!result ) {
2177
+ errno = err_win_to_posix (GetLastError ());
2178
+ return -1 ;
2179
+ }
2180
+
2181
+ /*
2182
+ * return absolute path if it fits within max_path (even if
2183
+ * "cwd + path" doesn't due to '..' components)
2184
+ */
2185
+ if (result < max_path ) {
2186
+ wcscpy (path , buf );
2187
+ return result ;
2188
+ }
2189
+
2190
+ /* error out if we shouldn't expand the path or buf is too small */
2191
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
2192
+ errno = ENAMETOOLONG ;
2193
+ return -1 ;
2194
+ }
2195
+
2196
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
2197
+ if (buf [0 ] == '\\' ) {
2198
+ /* ...unless already prefixed */
2199
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
2200
+ return len ;
2201
+
2202
+ wcscpy (path , L"\\\\?\\UNC\\" );
2203
+ wcscpy (path + 8 , buf + 2 );
2204
+ return result + 6 ;
2205
+ } else {
2206
+ wcscpy (path , L"\\\\?\\" );
2207
+ wcscpy (path + 4 , buf );
2208
+ return result + 4 ;
2209
+ }
2210
+ }
2211
+
2138
2212
/*
2139
2213
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
2140
2214
* mingw startup code, see init.c in mingw runtime).
@@ -2226,6 +2300,9 @@ void mingw_startup(void)
2226
2300
2227
2301
/* initialize Unicode console */
2228
2302
winansi_init ();
2303
+
2304
+ /* init length of current directory for handle_long_path */
2305
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
2229
2306
}
2230
2307
2231
2308
int uname (struct utsname * buf )
0 commit comments