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

Commit ace84e8

Browse files
committed
Merge pull request #122 from kblees/kb/long-paths-v2
Signed-off-by: Johannes Schindelin <[email protected]>
2 parents 4b5d776 + fce8d2c commit ace84e8

File tree

10 files changed

+373
-47
lines changed

10 files changed

+373
-47
lines changed

Documentation/config.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,13 @@ core.fscache::
630630
Git for Windows uses this to bulk-read and cache lstat data of entire
631631
directories (instead of doing lstat file by file).
632632

633+
core.longpaths::
634+
Enable long path (> 260) support for builtin commands in Git for
635+
Windows. This is disabled by default, as long paths are not supported
636+
by Windows Explorer, cmd.exe and the Git for Windows tool chain
637+
(msys, bash, tcl, perl...). Only enable this if you know what you're
638+
doing and are prepared to live with a few quirks.
639+
633640
core.createObject::
634641
You can set this to 'link', in which case a hardlink followed by
635642
a delete of the source are used to make sure that object creation

cache.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,8 @@ extern enum hide_dotfiles_type hide_dotfiles;
603603

604604
extern int core_fscache;
605605

606+
extern int core_long_paths;
607+
606608
enum branch_track {
607609
BRANCH_TRACK_UNSPECIFIED = -1,
608610
BRANCH_TRACK_NEVER = 0,

compat/mingw.c

Lines changed: 107 additions & 29 deletions
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)) {
@@ -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))
@@ -1119,6 +1130,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
11191130
si.hStdOutput = winansi_get_osfhandle(fhout);
11201131
si.hStdError = winansi_get_osfhandle(fherr);
11211132

1133+
/* executables and the current directory don't support long paths */
11221134
if (xutftowcs_path(wcmd, cmd) < 0)
11231135
return -1;
11241136
if (dir && xutftowcs_path(wdir, dir) < 0)
@@ -1611,8 +1623,9 @@ int mingw_rename(const char *pold, const char *pnew)
16111623
{
16121624
DWORD attrs, gle;
16131625
int tries = 0;
1614-
wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
1615-
if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0)
1626+
wchar_t wpold[MAX_LONG_PATH], wpnew[MAX_LONG_PATH];
1627+
if (xutftowcs_long_path(wpold, pold) < 0 ||
1628+
xutftowcs_long_path(wpnew, pnew) < 0)
16161629
return -1;
16171630

16181631
/*
@@ -1888,9 +1901,9 @@ int link(const char *oldpath, const char *newpath)
18881901
{
18891902
typedef BOOL (WINAPI *T)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES);
18901903
static T create_hard_link = NULL;
1891-
wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH];
1892-
if (xutftowcs_path(woldpath, oldpath) < 0 ||
1893-
xutftowcs_path(wnewpath, newpath) < 0)
1904+
wchar_t woldpath[MAX_LONG_PATH], wnewpath[MAX_LONG_PATH];
1905+
if (xutftowcs_long_path(woldpath, oldpath) < 0 ||
1906+
xutftowcs_long_path(wnewpath, newpath) < 0)
18941907
return -1;
18951908

18961909
if (!create_hard_link) {
@@ -2089,6 +2102,68 @@ int xwcstoutf(char *utf, const wchar_t *wcs, size_t utflen)
20892102
return -1;
20902103
}
20912104

2105+
int handle_long_path(wchar_t *path, int len, int max_path, int expand)
2106+
{
2107+
int result;
2108+
wchar_t buf[MAX_LONG_PATH];
2109+
2110+
/*
2111+
* we don't need special handling if path is relative to the current
2112+
* directory, and current directory + path don't exceed the desired
2113+
* max_path limit. This should cover > 99 % of cases with minimal
2114+
* performance impact (git almost always uses relative paths).
2115+
*/
2116+
if ((len < 2 || (!is_dir_sep(path[0]) && path[1] != ':')) &&
2117+
(current_directory_len + len < max_path))
2118+
return len;
2119+
2120+
/*
2121+
* handle everything else:
2122+
* - absolute paths: "C:\dir\file"
2123+
* - absolute UNC paths: "\\server\share\dir\file"
2124+
* - absolute paths on current drive: "\dir\file"
2125+
* - relative paths on other drive: "X:file"
2126+
* - prefixed paths: "\\?\...", "\\.\..."
2127+
*/
2128+
2129+
/* convert to absolute path using GetFullPathNameW */
2130+
result = GetFullPathNameW(path, MAX_LONG_PATH, buf, NULL);
2131+
if (!result) {
2132+
errno = err_win_to_posix(GetLastError());
2133+
return -1;
2134+
}
2135+
2136+
/*
2137+
* return absolute path if it fits within max_path (even if
2138+
* "cwd + path" doesn't due to '..' components)
2139+
*/
2140+
if (result < max_path) {
2141+
wcscpy(path, buf);
2142+
return result;
2143+
}
2144+
2145+
/* error out if we shouldn't expand the path or buf is too small */
2146+
if (!expand || result >= MAX_LONG_PATH - 6) {
2147+
errno = ENAMETOOLONG;
2148+
return -1;
2149+
}
2150+
2151+
/* prefix full path with "\\?\" or "\\?\UNC\" */
2152+
if (buf[0] == '\\') {
2153+
/* ...unless already prefixed */
2154+
if (buf[1] == '\\' && (buf[2] == '?' || buf[2] == '.'))
2155+
return len;
2156+
2157+
wcscpy(path, L"\\\\?\\UNC\\");
2158+
wcscpy(path + 8, buf + 2);
2159+
return result + 6;
2160+
} else {
2161+
wcscpy(path, L"\\\\?\\");
2162+
wcscpy(path + 4, buf);
2163+
return result + 4;
2164+
}
2165+
}
2166+
20922167
/*
20932168
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
20942169
* mingw startup code, see init.c in mingw runtime).
@@ -2192,4 +2267,7 @@ void mingw_startup()
21922267

21932268
/* initialize Unicode console */
21942269
winansi_init();
2270+
2271+
/* init length of current directory for handle_long_path */
2272+
current_directory_len = GetCurrentDirectoryW(0, NULL);
21952273
}

0 commit comments

Comments
 (0)