5
5
#include "../strbuf.h"
6
6
#include "../run-command.h"
7
7
#include "../cache.h"
8
- #include "../string-list.h"
9
8
10
9
static const int delay [] = { 0 , 1 , 10 , 20 , 40 };
11
10
unsigned int _CRT_fmode = _O_BINARY ;
@@ -205,8 +204,8 @@ static int ask_yes_no_if_possible(const char *format, ...)
205
204
int mingw_unlink (const char * pathname )
206
205
{
207
206
int ret , tries = 0 ;
208
- wchar_t wpathname [MAX_PATH ];
209
- if (xutftowcs_canonical_path (wpathname , pathname ) < 0 )
207
+ wchar_t wpathname [MAX_LONG_PATH ];
208
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
210
209
return -1 ;
211
210
212
211
/* read-only files cannot be removed */
@@ -235,7 +234,7 @@ static int is_dir_empty(const wchar_t *wpath)
235
234
{
236
235
WIN32_FIND_DATAW findbuf ;
237
236
HANDLE handle ;
238
- wchar_t wbuf [MAX_PATH + 2 ];
237
+ wchar_t wbuf [MAX_LONG_PATH + 2 ];
239
238
wcscpy (wbuf , wpath );
240
239
wcscat (wbuf , L"\\*" );
241
240
handle = FindFirstFileW (wbuf , & findbuf );
@@ -256,8 +255,8 @@ static int is_dir_empty(const wchar_t *wpath)
256
255
int mingw_rmdir (const char * pathname )
257
256
{
258
257
int ret , tries = 0 ;
259
- wchar_t wpathname [MAX_PATH ];
260
- if (xutftowcs_canonical_path (wpathname , pathname ) < 0 )
258
+ wchar_t wpathname [MAX_LONG_PATH ];
259
+ if (xutftowcs_long_path (wpathname , pathname ) < 0 )
261
260
return -1 ;
262
261
263
262
while ((ret = _wrmdir (wpathname )) == -1 && tries < ARRAY_SIZE (delay )) {
@@ -297,9 +296,9 @@ static int make_hidden(const wchar_t *path)
297
296
298
297
void mingw_mark_as_git_dir (const char * dir )
299
298
{
300
- wchar_t wdir [MAX_PATH ];
299
+ wchar_t wdir [MAX_LONG_PATH ];
301
300
if (hide_dotfiles != HIDE_DOTFILES_FALSE && !is_bare_repository ())
302
- if (xutftowcs_canonical_path (wdir , dir ) < 0 || make_hidden (wdir ))
301
+ if (xutftowcs_long_path (wdir , dir ) < 0 || make_hidden (wdir ))
303
302
warning ("Failed to make '%s' hidden" , dir );
304
303
git_config_set ("core.hideDotFiles" ,
305
304
hide_dotfiles == HIDE_DOTFILES_FALSE ? "false" :
@@ -310,9 +309,12 @@ void mingw_mark_as_git_dir(const char *dir)
310
309
int mingw_mkdir (const char * path , int mode )
311
310
{
312
311
int ret ;
313
- wchar_t wpath [MAX_PATH ];
314
- if (xutftowcs_canonical_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 )
315
316
return -1 ;
317
+
316
318
ret = _wmkdir (wpath );
317
319
if (!ret && hide_dotfiles == HIDE_DOTFILES_TRUE ) {
318
320
/*
@@ -332,7 +334,7 @@ int mingw_open (const char *filename, int oflags, ...)
332
334
va_list args ;
333
335
unsigned mode ;
334
336
int fd ;
335
- wchar_t wfilename [MAX_PATH ];
337
+ wchar_t wfilename [MAX_LONG_PATH ];
336
338
337
339
va_start (args , oflags );
338
340
mode = va_arg (args , int );
@@ -341,7 +343,7 @@ int mingw_open (const char *filename, int oflags, ...)
341
343
if (filename && !strcmp (filename , "/dev/null" ))
342
344
filename = "nul" ;
343
345
344
- if (xutftowcs_canonical_path (wfilename , filename ) < 0 )
346
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
345
347
return -1 ;
346
348
fd = _wopen (wfilename , oflags , mode );
347
349
@@ -406,66 +408,18 @@ int mingw_fgetc(FILE *stream)
406
408
return ch ;
407
409
}
408
410
409
- static struct string_list_item dos_device_names_items [] = {
410
- { "AUX" , NULL },
411
- { "CLOCK$" , NULL },
412
- { "COM1" , NULL },
413
- { "COM2" , NULL },
414
- { "COM3" , NULL },
415
- { "COM4" , NULL },
416
- { "CON" , NULL },
417
- { "CONERR$" , NULL },
418
- { "CONIN$" , NULL },
419
- { "CONOUT$" , NULL },
420
- { "LPT1" , NULL },
421
- { "LPT2" , NULL },
422
- { "LPT3" , NULL },
423
- { "NUL" , NULL },
424
- { "PRN" , NULL },
425
- { "aux" , NULL },
426
- { "clock$" , NULL },
427
- { "com1" , NULL },
428
- { "com2" , NULL },
429
- { "com3" , NULL },
430
- { "com4" , NULL },
431
- { "con" , NULL },
432
- { "conerr$" , NULL },
433
- { "conin$" , NULL },
434
- { "conout$" , NULL },
435
- { "lpt1" , NULL },
436
- { "lpt2" , NULL },
437
- { "lpt3" , NULL },
438
- { "nul" , NULL },
439
- { "prn" , NULL }
440
- };
441
-
442
- static struct string_list dos_device_names = {
443
- & dos_device_names_items [0 ], ARRAY_SIZE (dos_device_names_items ),
444
- 0 , 0 , NULL
445
- };
446
-
447
- static int is_dos_device_name (const char * filename )
448
- {
449
- return filename && !strchr (filename , '/' ) &&
450
- string_list_has_string (& dos_device_names , filename );
451
- }
452
-
453
411
#undef fopen
454
412
FILE * mingw_fopen (const char * filename , const char * otype )
455
413
{
456
414
int hide = 0 ;
457
415
FILE * file ;
458
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
459
-
460
- if (is_dos_device_name (filename ))
461
- return fopen (filename , otype );
462
-
416
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
463
417
if (hide_dotfiles == HIDE_DOTFILES_TRUE &&
464
418
basename ((char * )filename )[0 ] == '.' )
465
419
hide = access (filename , F_OK );
466
420
if (filename && !strcmp (filename , "/dev/null" ))
467
421
filename = "nul" ;
468
- if (xutftowcs_canonical_path (wfilename , filename ) < 0 ||
422
+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
469
423
xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
470
424
return NULL ;
471
425
file = _wfopen (wfilename , wotype );
@@ -474,22 +428,17 @@ FILE *mingw_fopen (const char *filename, const char *otype)
474
428
return file ;
475
429
}
476
430
477
- #undef freopen
478
431
FILE * mingw_freopen (const char * filename , const char * otype , FILE * stream )
479
432
{
480
433
int hide = 0 ;
481
434
FILE * file ;
482
- wchar_t wfilename [MAX_PATH ], wotype [4 ];
483
-
484
- if (is_dos_device_name (filename ))
485
- return freopen (filename , otype , stream );
486
-
435
+ wchar_t wfilename [MAX_LONG_PATH ], wotype [4 ];
487
436
if (hide_dotfiles == HIDE_DOTFILES_TRUE &&
488
437
basename ((char * )filename )[0 ] == '.' )
489
438
hide = access (filename , F_OK );
490
439
if (filename && !strcmp (filename , "/dev/null" ))
491
440
filename = "nul" ;
492
- if (xutftowcs_canonical_path (wfilename , filename ) < 0 ||
441
+ if (xutftowcs_long_path (wfilename , filename ) < 0 ||
493
442
xutftowcs (wotype , otype , ARRAY_SIZE (wotype )) < 0 )
494
443
return NULL ;
495
444
file = _wfreopen (wfilename , wotype , stream );
@@ -522,25 +471,32 @@ int mingw_fflush(FILE *stream)
522
471
523
472
int mingw_access (const char * filename , int mode )
524
473
{
525
- wchar_t wfilename [MAX_PATH ];
526
- if (xutftowcs_canonical_path (wfilename , filename ) < 0 )
474
+ wchar_t wfilename [MAX_LONG_PATH ];
475
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
527
476
return -1 ;
528
477
/* X_OK is not supported by the MSVCRT version */
529
478
return _waccess (wfilename , mode & ~X_OK );
530
479
}
531
480
481
+ /* cached length of current directory for handle_long_path */
482
+ static int current_directory_len = 0 ;
483
+
532
484
int mingw_chdir (const char * dirname )
533
485
{
486
+ int result ;
534
487
wchar_t wdirname [MAX_PATH ];
535
- if (xutftowcs_canonical_path (wdirname , dirname ) < 0 )
488
+ /* SetCurrentDirectoryW doesn't support long paths */
489
+ if (xutftowcs_path (wdirname , dirname ) < 0 )
536
490
return -1 ;
537
- return _wchdir (wdirname );
491
+ result = _wchdir (wdirname );
492
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
493
+ return result ;
538
494
}
539
495
540
496
int mingw_chmod (const char * filename , int mode )
541
497
{
542
- wchar_t wfilename [MAX_PATH ];
543
- if (xutftowcs_canonical_path (wfilename , filename ) < 0 )
498
+ wchar_t wfilename [MAX_LONG_PATH ];
499
+ if (xutftowcs_long_path (wfilename , filename ) < 0 )
544
500
return -1 ;
545
501
return _wchmod (wfilename , mode );
546
502
}
@@ -555,8 +511,8 @@ int mingw_chmod(const char *filename, int mode)
555
511
static int do_lstat (int follow , const char * file_name , struct stat * buf )
556
512
{
557
513
WIN32_FILE_ATTRIBUTE_DATA fdata ;
558
- wchar_t wfilename [MAX_PATH ];
559
- if (xutftowcs_canonical_path (wfilename , file_name ) < 0 )
514
+ wchar_t wfilename [MAX_LONG_PATH ];
515
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
560
516
return -1 ;
561
517
562
518
if (GetFileAttributesExW (wfilename , GetFileExInfoStandard , & fdata )) {
@@ -699,8 +655,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
699
655
FILETIME mft , aft ;
700
656
int fh , rc ;
701
657
DWORD attrs ;
702
- wchar_t wfilename [MAX_PATH ];
703
- if (xutftowcs_canonical_path (wfilename , file_name ) < 0 )
658
+ wchar_t wfilename [MAX_LONG_PATH ];
659
+ if (xutftowcs_long_path (wfilename , file_name ) < 0 )
704
660
return -1 ;
705
661
706
662
/* must have write permission */
@@ -748,6 +704,7 @@ unsigned int sleep (unsigned int seconds)
748
704
char * mingw_mktemp (char * template )
749
705
{
750
706
wchar_t wtemplate [MAX_PATH ];
707
+ /* we need to return the path, thus no long paths here! */
751
708
if (xutftowcs_path (wtemplate , template ) < 0 )
752
709
return NULL ;
753
710
if (!_wmktemp (wtemplate ))
@@ -1190,6 +1147,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
1190
1147
si .hStdOutput = winansi_get_osfhandle (fhout );
1191
1148
si .hStdError = winansi_get_osfhandle (fherr );
1192
1149
1150
+ /* executables and the current directory don't support long paths */
1193
1151
if (xutftowcs_path (wcmd , cmd ) < 0 )
1194
1152
return -1 ;
1195
1153
if (dir && xutftowcs_path (wdir , dir ) < 0 )
@@ -1682,9 +1640,9 @@ int mingw_rename(const char *pold, const char *pnew)
1682
1640
{
1683
1641
DWORD attrs , gle ;
1684
1642
int tries = 0 ;
1685
- wchar_t wpold [MAX_PATH ], wpnew [MAX_PATH ];
1686
- if (xutftowcs_canonical_path (wpold , pold ) < 0 ||
1687
- xutftowcs_canonical_path (wpnew , pnew ) < 0 )
1643
+ wchar_t wpold [MAX_LONG_PATH ], wpnew [MAX_LONG_PATH ];
1644
+ if (xutftowcs_long_path (wpold , pold ) < 0 ||
1645
+ xutftowcs_long_path (wpnew , pnew ) < 0 )
1688
1646
return -1 ;
1689
1647
1690
1648
/*
@@ -1960,9 +1918,9 @@ int link(const char *oldpath, const char *newpath)
1960
1918
{
1961
1919
typedef BOOL (WINAPI * T )(LPCWSTR , LPCWSTR , LPSECURITY_ATTRIBUTES );
1962
1920
static T create_hard_link = NULL ;
1963
- wchar_t woldpath [MAX_PATH ], wnewpath [MAX_PATH ];
1964
- if (xutftowcs_canonical_path (woldpath , oldpath ) < 0 ||
1965
- xutftowcs_canonical_path (wnewpath , newpath ) < 0 )
1921
+ wchar_t woldpath [MAX_LONG_PATH ], wnewpath [MAX_LONG_PATH ];
1922
+ if (xutftowcs_long_path (woldpath , oldpath ) < 0 ||
1923
+ xutftowcs_long_path (wnewpath , newpath ) < 0 )
1966
1924
return -1 ;
1967
1925
1968
1926
if (!create_hard_link ) {
@@ -2161,6 +2119,68 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
2161
2119
return -1 ;
2162
2120
}
2163
2121
2122
+ int handle_long_path (wchar_t * path , int len , int max_path , int expand )
2123
+ {
2124
+ int result ;
2125
+ wchar_t buf [MAX_LONG_PATH ];
2126
+
2127
+ /*
2128
+ * we don't need special handling if path is relative to the current
2129
+ * directory, and current directory + path don't exceed the desired
2130
+ * max_path limit. This should cover > 99 % of cases with minimal
2131
+ * performance impact (git almost always uses relative paths).
2132
+ */
2133
+ if ((len < 2 || (!is_dir_sep (path [0 ]) && path [1 ] != ':' )) &&
2134
+ (current_directory_len + len < max_path ))
2135
+ return len ;
2136
+
2137
+ /*
2138
+ * handle everything else:
2139
+ * - absolute paths: "C:\dir\file"
2140
+ * - absolute UNC paths: "\\server\share\dir\file"
2141
+ * - absolute paths on current drive: "\dir\file"
2142
+ * - relative paths on other drive: "X:file"
2143
+ * - prefixed paths: "\\?\...", "\\.\..."
2144
+ */
2145
+
2146
+ /* convert to absolute path using GetFullPathNameW */
2147
+ result = GetFullPathNameW (path , MAX_LONG_PATH , buf , NULL );
2148
+ if (!result ) {
2149
+ errno = err_win_to_posix (GetLastError ());
2150
+ return -1 ;
2151
+ }
2152
+
2153
+ /*
2154
+ * return absolute path if it fits within max_path (even if
2155
+ * "cwd + path" doesn't due to '..' components)
2156
+ */
2157
+ if (result < max_path ) {
2158
+ wcscpy (path , buf );
2159
+ return result ;
2160
+ }
2161
+
2162
+ /* error out if we shouldn't expand the path or buf is too small */
2163
+ if (!expand || result >= MAX_LONG_PATH - 6 ) {
2164
+ errno = ENAMETOOLONG ;
2165
+ return -1 ;
2166
+ }
2167
+
2168
+ /* prefix full path with "\\?\" or "\\?\UNC\" */
2169
+ if (buf [0 ] == '\\' ) {
2170
+ /* ...unless already prefixed */
2171
+ if (buf [1 ] == '\\' && (buf [2 ] == '?' || buf [2 ] == '.' ))
2172
+ return len ;
2173
+
2174
+ wcscpy (path , L"\\\\?\\UNC\\" );
2175
+ wcscpy (path + 8 , buf + 2 );
2176
+ return result + 6 ;
2177
+ } else {
2178
+ wcscpy (path , L"\\\\?\\" );
2179
+ wcscpy (path + 4 , buf );
2180
+ return result + 4 ;
2181
+ }
2182
+ }
2183
+
2164
2184
/*
2165
2185
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
2166
2186
* mingw startup code, see init.c in mingw runtime).
@@ -2264,4 +2284,7 @@ void mingw_startup()
2264
2284
2265
2285
/* initialize Unicode console */
2266
2286
winansi_init ();
2287
+
2288
+ /* init length of current directory for handle_long_path */
2289
+ current_directory_len = GetCurrentDirectoryW (0 , NULL );
2267
2290
}
0 commit comments