Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Commit 98ed09f

Browse files
committed
Merge pull request #122 from kblees/kb/long-paths-v2
Signed-off-by: Johannes Schindelin <[email protected]>
2 parents 41bc59c + 5d0b4ec commit 98ed09f

File tree

10 files changed

+418
-49
lines changed

10 files changed

+418
-49
lines changed

Documentation/config.txt

+7
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,13 @@ core.fscache::
637637
Git for Windows uses this to bulk-read and cache lstat data of entire
638638
directories (instead of doing lstat file by file).
639639

640+
core.longpaths::
641+
Enable long path (> 260) support for builtin commands in Git for
642+
Windows. This is disabled by default, as long paths are not supported
643+
by Windows Explorer, cmd.exe and the Git for Windows tool chain
644+
(msys, bash, tcl, perl...). Only enable this if you know what you're
645+
doing and are prepared to live with a few quirks.
646+
640647
core.createObject::
641648
You can set this to 'link', in which case a hardlink followed by
642649
a delete of the source are used to make sure that object creation

cache.h

+2
Original file line numberDiff line numberDiff line change
@@ -650,6 +650,8 @@ extern enum hide_dotfiles_type hide_dotfiles;
650650

651651
extern int core_fscache;
652652

653+
extern int core_long_paths;
654+
653655
enum branch_track {
654656
BRANCH_TRACK_UNSPECIFIED = -1,
655657
BRANCH_TRACK_NEVER = 0,

compat/mingw.c

+109-31
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,8 @@ static int ask_yes_no_if_possible(const char *format, ...)
204204
int mingw_unlink(const char *pathname)
205205
{
206206
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)
209209
return -1;
210210

211211
/* read-only files cannot be removed */
@@ -234,7 +234,7 @@ static int is_dir_empty(const wchar_t *wpath)
234234
{
235235
WIN32_FIND_DATAW findbuf;
236236
HANDLE handle;
237-
wchar_t wbuf[MAX_PATH + 2];
237+
wchar_t wbuf[MAX_LONG_PATH + 2];
238238
wcscpy(wbuf, wpath);
239239
wcscat(wbuf, L"\\*");
240240
handle = FindFirstFileW(wbuf, &findbuf);
@@ -255,8 +255,8 @@ static int is_dir_empty(const wchar_t *wpath)
255255
int mingw_rmdir(const char *pathname)
256256
{
257257
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)
260260
return -1;
261261

262262
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
@@ -296,9 +296,9 @@ static int make_hidden(const wchar_t *path)
296296

297297
void mingw_mark_as_git_dir(const char *dir)
298298
{
299-
wchar_t wdir[MAX_PATH];
299+
wchar_t wdir[MAX_LONG_PATH];
300300
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))
302302
warning("Failed to make '%s' hidden", dir);
303303
git_config_set("core.hideDotFiles",
304304
hide_dotfiles == HIDE_DOTFILES_FALSE ? "false" :
@@ -309,9 +309,12 @@ void mingw_mark_as_git_dir(const char *dir)
309309
int mingw_mkdir(const char *path, int mode)
310310
{
311311
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)
314316
return -1;
317+
315318
ret = _wmkdir(wpath);
316319
if (!ret && hide_dotfiles == HIDE_DOTFILES_TRUE) {
317320
/*
@@ -331,7 +334,7 @@ int mingw_open (const char *filename, int oflags, ...)
331334
va_list args;
332335
unsigned mode;
333336
int fd;
334-
wchar_t wfilename[MAX_PATH];
337+
wchar_t wfilename[MAX_LONG_PATH];
335338

336339
va_start(args, oflags);
337340
mode = va_arg(args, int);
@@ -340,7 +343,7 @@ int mingw_open (const char *filename, int oflags, ...)
340343
if (filename && !strcmp(filename, "/dev/null"))
341344
filename = "nul";
342345

343-
if (xutftowcs_path(wfilename, filename) < 0)
346+
if (xutftowcs_long_path(wfilename, filename) < 0)
344347
return -1;
345348
fd = _wopen(wfilename, oflags, mode);
346349

@@ -393,13 +396,13 @@ FILE *mingw_fopen (const char *filename, const char *otype)
393396
{
394397
int hide = 0;
395398
FILE *file;
396-
wchar_t wfilename[MAX_PATH], wotype[4];
399+
wchar_t wfilename[MAX_LONG_PATH], wotype[4];
397400
if (hide_dotfiles == HIDE_DOTFILES_TRUE &&
398401
basename((char*)filename)[0] == '.')
399402
hide = access(filename, F_OK);
400403
if (filename && !strcmp(filename, "/dev/null"))
401404
filename = "nul";
402-
if (xutftowcs_path(wfilename, filename) < 0 ||
405+
if (xutftowcs_long_path(wfilename, filename) < 0 ||
403406
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
404407
return NULL;
405408
file = _wfopen(wfilename, wotype);
@@ -412,13 +415,13 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
412415
{
413416
int hide = 0;
414417
FILE *file;
415-
wchar_t wfilename[MAX_PATH], wotype[4];
418+
wchar_t wfilename[MAX_LONG_PATH], wotype[4];
416419
if (hide_dotfiles == HIDE_DOTFILES_TRUE &&
417420
basename((char*)filename)[0] == '.')
418421
hide = access(filename, F_OK);
419422
if (filename && !strcmp(filename, "/dev/null"))
420423
filename = "nul";
421-
if (xutftowcs_path(wfilename, filename) < 0 ||
424+
if (xutftowcs_long_path(wfilename, filename) < 0 ||
422425
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
423426
return NULL;
424427
file = _wfreopen(wfilename, wotype, stream);
@@ -451,25 +454,32 @@ int mingw_fflush(FILE *stream)
451454

452455
int mingw_access(const char *filename, int mode)
453456
{
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)
456459
return -1;
457460
/* X_OK is not supported by the MSVCRT version */
458461
return _waccess(wfilename, mode & ~X_OK);
459462
}
460463

464+
/* cached length of current directory for handle_long_path */
465+
static int current_directory_len = 0;
466+
461467
int mingw_chdir(const char *dirname)
462468
{
469+
int result;
463470
wchar_t wdirname[MAX_PATH];
471+
/* SetCurrentDirectoryW doesn't support long paths */
464472
if (xutftowcs_path(wdirname, dirname) < 0)
465473
return -1;
466-
return _wchdir(wdirname);
474+
result = _wchdir(wdirname);
475+
current_directory_len = GetCurrentDirectoryW(0, NULL);
476+
return result;
467477
}
468478

469479
int mingw_chmod(const char *filename, int mode)
470480
{
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)
473483
return -1;
474484
return _wchmod(wfilename, mode);
475485
}
@@ -484,8 +494,8 @@ int mingw_chmod(const char *filename, int mode)
484494
static int do_lstat(int follow, const char *file_name, struct stat *buf)
485495
{
486496
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)
489499
return -1;
490500

491501
if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
@@ -550,7 +560,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
550560
static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
551561
{
552562
int namelen;
553-
char alt_name[PATH_MAX];
563+
char alt_name[MAX_LONG_PATH];
554564

555565
if (!do_lstat(follow, file_name, buf))
556566
return 0;
@@ -566,7 +576,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
566576
return -1;
567577
while (namelen && file_name[namelen-1] == '/')
568578
--namelen;
569-
if (!namelen || namelen >= PATH_MAX)
579+
if (!namelen || namelen >= MAX_LONG_PATH)
570580
return -1;
571581

572582
memcpy(alt_name, file_name, namelen);
@@ -628,8 +638,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
628638
FILETIME mft, aft;
629639
int fh, rc;
630640
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)
633643
return -1;
634644

635645
/* must have write permission */
@@ -677,6 +687,7 @@ unsigned int sleep (unsigned int seconds)
677687
char *mingw_mktemp(char *template)
678688
{
679689
wchar_t wtemplate[MAX_PATH];
690+
/* we need to return the path, thus no long paths here! */
680691
if (xutftowcs_path(wtemplate, template) < 0)
681692
return NULL;
682693
if (!_wmktemp(wtemplate))
@@ -1037,6 +1048,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
10371048
si.hStdOutput = winansi_get_osfhandle(fhout);
10381049
si.hStdError = winansi_get_osfhandle(fherr);
10391050

1051+
/* executables and the current directory don't support long paths */
10401052
if (xutftowcs_path(wcmd, cmd) < 0)
10411053
return -1;
10421054
if (dir && xutftowcs_path(wdir, dir) < 0)
@@ -1612,8 +1624,9 @@ int mingw_rename(const char *pold, const char *pnew)
16121624
{
16131625
DWORD attrs, gle;
16141626
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)
16171630
return -1;
16181631

16191632
/*
@@ -1889,9 +1902,9 @@ int link(const char *oldpath, const char *newpath)
18891902
{
18901903
typedef BOOL (WINAPI *T)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
18911904
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)
18951908
return -1;
18961909

18971910
if (!create_hard_link) {
@@ -2072,6 +2085,68 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
20722085
return -1;
20732086
}
20742087

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+
20752150
/*
20762151
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
20772152
* mingw startup code, see init.c in mingw runtime).
@@ -2175,4 +2250,7 @@ void mingw_startup()
21752250

21762251
/* initialize Unicode console */
21772252
winansi_init();
2253+
2254+
/* init length of current directory for handle_long_path */
2255+
current_directory_len = GetCurrentDirectoryW(0, NULL);
21782256
}

0 commit comments

Comments
 (0)