Skip to content

Commit b800031

Browse files
committed
Merge branch 'long-paths'
2 parents 37f6235 + df1e5e2 commit b800031

File tree

7 files changed

+458
-56
lines changed

7 files changed

+458
-56
lines changed

Documentation/config/core.txt

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

679+
core.longpaths::
680+
Enable long path (> 260) support for builtin commands in Git for
681+
Windows. This is disabled by default, as long paths are not supported
682+
by Windows Explorer, cmd.exe and the Git for Windows tool chain
683+
(msys, bash, tcl, perl...). Only enable this if you know what you're
684+
doing and are prepared to live with a few quirks.
685+
679686
core.unsetenvvars::
680687
Windows-only: comma-separated list of environment variables'
681688
names that need to be unset before spawning any other process.

compat/mingw.c

Lines changed: 142 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,27 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
247247
static char *unset_environment_variables;
248248
int core_fscache;
249249

250+
int are_long_paths_enabled(void)
251+
{
252+
/* default to `false` during initialization */
253+
static const int fallback = 0;
254+
255+
static int enabled = -1;
256+
257+
if (enabled < 0) {
258+
/* avoid infinite recursion */
259+
if (!the_repository)
260+
return fallback;
261+
262+
if (the_repository->config &&
263+
the_repository->config->hash_initialized &&
264+
git_config_get_bool("core.longpaths", &enabled) < 0)
265+
enabled = 0;
266+
}
267+
268+
return enabled < 0 ? fallback : enabled;
269+
}
270+
250271
int mingw_core_config(const char *var, const char *value,
251272
const struct config_context *ctx, void *cb)
252273
{
@@ -311,8 +332,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
311332
int mingw_unlink(const char *pathname)
312333
{
313334
int ret, tries = 0;
314-
wchar_t wpathname[MAX_PATH];
315-
if (xutftowcs_path(wpathname, pathname) < 0)
335+
wchar_t wpathname[MAX_LONG_PATH];
336+
if (xutftowcs_long_path(wpathname, pathname) < 0)
316337
return -1;
317338

318339
if (DeleteFileW(wpathname))
@@ -344,7 +365,7 @@ static int is_dir_empty(const wchar_t *wpath)
344365
{
345366
WIN32_FIND_DATAW findbuf;
346367
HANDLE handle;
347-
wchar_t wbuf[MAX_PATH + 2];
368+
wchar_t wbuf[MAX_LONG_PATH + 2];
348369
wcscpy(wbuf, wpath);
349370
wcscat(wbuf, L"\\*");
350371
handle = FindFirstFileW(wbuf, &findbuf);
@@ -365,7 +386,7 @@ static int is_dir_empty(const wchar_t *wpath)
365386
int mingw_rmdir(const char *pathname)
366387
{
367388
int ret, tries = 0;
368-
wchar_t wpathname[MAX_PATH];
389+
wchar_t wpathname[MAX_LONG_PATH];
369390
struct stat st;
370391

371392
/*
@@ -387,7 +408,7 @@ int mingw_rmdir(const char *pathname)
387408
return -1;
388409
}
389410

390-
if (xutftowcs_path(wpathname, pathname) < 0)
411+
if (xutftowcs_long_path(wpathname, pathname) < 0)
391412
return -1;
392413

393414
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
@@ -466,15 +487,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
466487
int mingw_mkdir(const char *path, int mode)
467488
{
468489
int ret;
469-
wchar_t wpath[MAX_PATH];
490+
wchar_t wpath[MAX_LONG_PATH];
470491

471492
if (!is_valid_win32_path(path, 0)) {
472493
errno = EINVAL;
473494
return -1;
474495
}
475496

476-
if (xutftowcs_path(wpath, path) < 0)
497+
/* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
498+
if (xutftowcs_path_ex(wpath, path, MAX_LONG_PATH, -1, 248,
499+
are_long_paths_enabled()) < 0)
477500
return -1;
501+
478502
ret = _wmkdir(wpath);
479503
if (!ret && needs_hiding(path))
480504
return set_hidden_flag(wpath, 1);
@@ -561,7 +585,7 @@ int mingw_open (const char *filename, int oflags, ...)
561585
va_list args;
562586
unsigned mode;
563587
int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
564-
wchar_t wfilename[MAX_PATH];
588+
wchar_t wfilename[MAX_LONG_PATH];
565589
open_fn_t open_fn;
566590

567591
va_start(args, oflags);
@@ -589,7 +613,7 @@ int mingw_open (const char *filename, int oflags, ...)
589613

590614
if (filename && !strcmp(filename, "/dev/null"))
591615
wcscpy(wfilename, L"nul");
592-
else if (xutftowcs_path(wfilename, filename) < 0)
616+
else if (xutftowcs_long_path(wfilename, filename) < 0)
593617
return -1;
594618

595619
fd = open_fn(wfilename, oflags, mode);
@@ -647,14 +671,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
647671
{
648672
int hide = needs_hiding(filename);
649673
FILE *file;
650-
wchar_t wfilename[MAX_PATH], wotype[4];
674+
wchar_t wfilename[MAX_LONG_PATH], wotype[4];
651675
if (filename && !strcmp(filename, "/dev/null"))
652676
wcscpy(wfilename, L"nul");
653677
else if (!is_valid_win32_path(filename, 1)) {
654678
int create = otype && strchr(otype, 'w');
655679
errno = create ? EINVAL : ENOENT;
656680
return NULL;
657-
} else if (xutftowcs_path(wfilename, filename) < 0)
681+
} else if (xutftowcs_long_path(wfilename, filename) < 0)
658682
return NULL;
659683

660684
if (xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
@@ -676,14 +700,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
676700
{
677701
int hide = needs_hiding(filename);
678702
FILE *file;
679-
wchar_t wfilename[MAX_PATH], wotype[4];
703+
wchar_t wfilename[MAX_LONG_PATH], wotype[4];
680704
if (filename && !strcmp(filename, "/dev/null"))
681705
wcscpy(wfilename, L"nul");
682706
else if (!is_valid_win32_path(filename, 1)) {
683707
int create = otype && strchr(otype, 'w');
684708
errno = create ? EINVAL : ENOENT;
685709
return NULL;
686-
} else if (xutftowcs_path(wfilename, filename) < 0)
710+
} else if (xutftowcs_long_path(wfilename, filename) < 0)
687711
return NULL;
688712

689713
if (xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
@@ -733,7 +757,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
733757
HANDLE h = (HANDLE) _get_osfhandle(fd);
734758
if (GetFileType(h) != FILE_TYPE_PIPE) {
735759
if (orig == EINVAL) {
736-
wchar_t path[MAX_PATH];
760+
wchar_t path[MAX_LONG_PATH];
737761
DWORD ret = GetFinalPathNameByHandleW(h, path,
738762
ARRAY_SIZE(path), 0);
739763
UINT drive_type = ret > 0 && ret < ARRAY_SIZE(path) ?
@@ -770,27 +794,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
770794

771795
int mingw_access(const char *filename, int mode)
772796
{
773-
wchar_t wfilename[MAX_PATH];
797+
wchar_t wfilename[MAX_LONG_PATH];
774798
if (!strcmp("nul", filename) || !strcmp("/dev/null", filename))
775799
return 0;
776-
if (xutftowcs_path(wfilename, filename) < 0)
800+
if (xutftowcs_long_path(wfilename, filename) < 0)
777801
return -1;
778802
/* X_OK is not supported by the MSVCRT version */
779803
return _waccess(wfilename, mode & ~X_OK);
780804
}
781805

806+
/* cached length of current directory for handle_long_path */
807+
static int current_directory_len = 0;
808+
782809
int mingw_chdir(const char *dirname)
783810
{
784-
wchar_t wdirname[MAX_PATH];
785-
if (xutftowcs_path(wdirname, dirname) < 0)
811+
int result;
812+
wchar_t wdirname[MAX_LONG_PATH];
813+
if (xutftowcs_long_path(wdirname, dirname) < 0)
786814
return -1;
787-
return _wchdir(wdirname);
815+
result = _wchdir(wdirname);
816+
current_directory_len = GetCurrentDirectoryW(0, NULL);
817+
return result;
788818
}
789819

790820
int mingw_chmod(const char *filename, int mode)
791821
{
792-
wchar_t wfilename[MAX_PATH];
793-
if (xutftowcs_path(wfilename, filename) < 0)
822+
wchar_t wfilename[MAX_LONG_PATH];
823+
if (xutftowcs_long_path(wfilename, filename) < 0)
794824
return -1;
795825
return _wchmod(wfilename, mode);
796826
}
@@ -838,8 +868,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
838868
static int do_lstat(int follow, const char *file_name, struct stat *buf)
839869
{
840870
WIN32_FILE_ATTRIBUTE_DATA fdata;
841-
wchar_t wfilename[MAX_PATH];
842-
if (xutftowcs_path(wfilename, file_name) < 0)
871+
wchar_t wfilename[MAX_LONG_PATH];
872+
if (xutftowcs_long_path(wfilename, file_name) < 0)
843873
return -1;
844874

845875
if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
@@ -910,7 +940,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
910940
static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
911941
{
912942
int namelen;
913-
char alt_name[PATH_MAX];
943+
char alt_name[MAX_LONG_PATH];
914944

915945
if (!do_lstat(follow, file_name, buf))
916946
return 0;
@@ -926,7 +956,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
926956
return -1;
927957
while (namelen && file_name[namelen-1] == '/')
928958
--namelen;
929-
if (!namelen || namelen >= PATH_MAX)
959+
if (!namelen || namelen >= MAX_LONG_PATH)
930960
return -1;
931961

932962
memcpy(alt_name, file_name, namelen);
@@ -1010,10 +1040,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
10101040
FILETIME mft, aft;
10111041
int rc;
10121042
DWORD attrs;
1013-
wchar_t wfilename[MAX_PATH];
1043+
wchar_t wfilename[MAX_LONG_PATH];
10141044
HANDLE osfilehandle;
10151045

1016-
if (xutftowcs_path(wfilename, file_name) < 0)
1046+
if (xutftowcs_long_path(wfilename, file_name) < 0)
10171047
return -1;
10181048

10191049
/* must have write permission */
@@ -1096,6 +1126,7 @@ char *mingw_mktemp(char *template)
10961126
wchar_t wtemplate[MAX_PATH];
10971127
int offset = 0;
10981128

1129+
/* we need to return the path, thus no long paths here! */
10991130
if (xutftowcs_path(wtemplate, template) < 0)
11001131
return NULL;
11011132

@@ -1747,6 +1778,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
17471778

17481779
if (*argv && !strcmp(cmd, *argv))
17491780
wcmd[0] = L'\0';
1781+
/*
1782+
* Paths to executables and to the current directory do not support
1783+
* long paths, therefore we cannot use xutftowcs_long_path() here.
1784+
*/
17501785
else if (xutftowcs_path(wcmd, cmd) < 0)
17511786
return -1;
17521787
if (dir && xutftowcs_path(wdir, dir) < 0)
@@ -2395,8 +2430,9 @@ int mingw_rename(const char *pold, const char *pnew)
23952430
{
23962431
DWORD attrs, gle;
23972432
int tries = 0;
2398-
wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
2399-
if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0)
2433+
wchar_t wpold[MAX_LONG_PATH], wpnew[MAX_LONG_PATH];
2434+
if (xutftowcs_long_path(wpold, pold) < 0 ||
2435+
xutftowcs_long_path(wpnew, pnew) < 0)
24002436
return -1;
24012437

24022438
/*
@@ -2710,9 +2746,9 @@ int mingw_raise(int sig)
27102746

27112747
int link(const char *oldpath, const char *newpath)
27122748
{
2713-
wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH];
2714-
if (xutftowcs_path(woldpath, oldpath) < 0 ||
2715-
xutftowcs_path(wnewpath, newpath) < 0)
2749+
wchar_t woldpath[MAX_LONG_PATH], wnewpath[MAX_LONG_PATH];
2750+
if (xutftowcs_long_path(woldpath, oldpath) < 0 ||
2751+
xutftowcs_long_path(wnewpath, newpath) < 0)
27162752
return -1;
27172753

27182754
if (!CreateHardLinkW(wnewpath, woldpath, NULL)) {
@@ -2780,8 +2816,8 @@ int mingw_is_mount_point(struct strbuf *path)
27802816
{
27812817
WIN32_FIND_DATAW findbuf = { 0 };
27822818
HANDLE handle;
2783-
wchar_t wfilename[MAX_PATH];
2784-
int wlen = xutftowcs_path(wfilename, path->buf);
2819+
wchar_t wfilename[MAX_LONG_PATH];
2820+
int wlen = xutftowcs_long_path(wfilename, path->buf);
27852821
if (wlen < 0)
27862822
die(_("could not get long path for '%s'"), path->buf);
27872823

@@ -2926,9 +2962,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
29262962

29272963
static int is_system32_path(const char *path)
29282964
{
2929-
WCHAR system32[MAX_PATH], wpath[MAX_PATH];
2965+
WCHAR system32[MAX_LONG_PATH], wpath[MAX_LONG_PATH];
29302966

2931-
if (xutftowcs_path(wpath, path) < 0 ||
2967+
if (xutftowcs_long_path(wpath, path) < 0 ||
29322968
!GetSystemDirectoryW(system32, ARRAY_SIZE(system32)) ||
29332969
_wcsicmp(system32, wpath))
29342970
return 0;
@@ -3340,6 +3376,73 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
33403376
}
33413377
}
33423378

3379+
int handle_long_path(wchar_t *path, int len, int max_path, int expand)
3380+
{
3381+
int result;
3382+
wchar_t buf[MAX_LONG_PATH];
3383+
3384+
/*
3385+
* we don't need special handling if path is relative to the current
3386+
* directory, and current directory + path don't exceed the desired
3387+
* max_path limit. This should cover > 99 % of cases with minimal
3388+
* performance impact (git almost always uses relative paths).
3389+
*/
3390+
if ((len < 2 || (!is_dir_sep(path[0]) && path[1] != ':')) &&
3391+
(current_directory_len + len < max_path))
3392+
return len;
3393+
3394+
/*
3395+
* handle everything else:
3396+
* - absolute paths: "C:\dir\file"
3397+
* - absolute UNC paths: "\\server\share\dir\file"
3398+
* - absolute paths on current drive: "\dir\file"
3399+
* - relative paths on other drive: "X:file"
3400+
* - prefixed paths: "\\?\...", "\\.\..."
3401+
*/
3402+
3403+
/* convert to absolute path using GetFullPathNameW */
3404+
result = GetFullPathNameW(path, MAX_LONG_PATH, buf, NULL);
3405+
if (!result) {
3406+
errno = err_win_to_posix(GetLastError());
3407+
return -1;
3408+
}
3409+
3410+
/*
3411+
* return absolute path if it fits within max_path (even if
3412+
* "cwd + path" doesn't due to '..' components)
3413+
*/
3414+
if (result < max_path) {
3415+
/* Be careful not to add a drive prefix if there was none */
3416+
if (is_wdir_sep(path[0]) &&
3417+
!is_wdir_sep(buf[0]) && buf[1] == L':' && is_wdir_sep(buf[2]))
3418+
wcscpy(path, buf + 2);
3419+
else
3420+
wcscpy(path, buf);
3421+
return result;
3422+
}
3423+
3424+
/* error out if we shouldn't expand the path or buf is too small */
3425+
if (!expand || result >= MAX_LONG_PATH - 6) {
3426+
errno = ENAMETOOLONG;
3427+
return -1;
3428+
}
3429+
3430+
/* prefix full path with "\\?\" or "\\?\UNC\" */
3431+
if (buf[0] == '\\') {
3432+
/* ...unless already prefixed */
3433+
if (buf[1] == '\\' && (buf[2] == '?' || buf[2] == '.'))
3434+
return len;
3435+
3436+
wcscpy(path, L"\\\\?\\UNC\\");
3437+
wcscpy(path + 8, buf + 2);
3438+
return result + 6;
3439+
} else {
3440+
wcscpy(path, L"\\\\?\\");
3441+
wcscpy(path + 4, buf);
3442+
return result + 4;
3443+
}
3444+
}
3445+
33433446
#if !defined(_MSC_VER)
33443447
/*
33453448
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3501,6 +3604,9 @@ int wmain(int argc, const wchar_t **wargv)
35013604
/* initialize Unicode console */
35023605
winansi_init();
35033606

3607+
/* init length of current directory for handle_long_path */
3608+
current_directory_len = GetCurrentDirectoryW(0, NULL);
3609+
35043610
/* invoke the real main() using our utf8 version of argv. */
35053611
exit_status = main(argc, argv);
35063612

0 commit comments

Comments
 (0)