Skip to content

Commit 8c51797

Browse files
committed
Merge branch 'long-paths'
2 parents ecd97c6 + 540e768 commit 8c51797

File tree

7 files changed

+445
-55
lines changed

7 files changed

+445
-55
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: 136 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,27 @@ static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
239239
static char *unset_environment_variables;
240240
int core_fscache;
241241

242+
int are_long_paths_enabled(void)
243+
{
244+
/* default to `false` during initialization */
245+
static const int fallback = 0;
246+
247+
static int enabled = -1;
248+
249+
if (enabled < 0) {
250+
/* avoid infinite recursion */
251+
if (!the_repository)
252+
return fallback;
253+
254+
if (the_repository->config &&
255+
the_repository->config->hash_initialized &&
256+
git_config_get_bool("core.longpaths", &enabled) < 0)
257+
enabled = 0;
258+
}
259+
260+
return enabled < 0 ? fallback : enabled;
261+
}
262+
242263
int mingw_core_config(const char *var, const char *value, void *cb)
243264
{
244265
if (!strcmp(var, "core.hidedotfiles")) {
@@ -300,8 +321,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
300321
int mingw_unlink(const char *pathname)
301322
{
302323
int ret, tries = 0;
303-
wchar_t wpathname[MAX_PATH];
304-
if (xutftowcs_path(wpathname, pathname) < 0)
324+
wchar_t wpathname[MAX_LONG_PATH];
325+
if (xutftowcs_long_path(wpathname, pathname) < 0)
305326
return -1;
306327

307328
if (DeleteFileW(wpathname))
@@ -333,7 +354,7 @@ static int is_dir_empty(const wchar_t *wpath)
333354
{
334355
WIN32_FIND_DATAW findbuf;
335356
HANDLE handle;
336-
wchar_t wbuf[MAX_PATH + 2];
357+
wchar_t wbuf[MAX_LONG_PATH + 2];
337358
wcscpy(wbuf, wpath);
338359
wcscat(wbuf, L"\\*");
339360
handle = FindFirstFileW(wbuf, &findbuf);
@@ -354,7 +375,7 @@ static int is_dir_empty(const wchar_t *wpath)
354375
int mingw_rmdir(const char *pathname)
355376
{
356377
int ret, tries = 0;
357-
wchar_t wpathname[MAX_PATH];
378+
wchar_t wpathname[MAX_LONG_PATH];
358379
struct stat st;
359380

360381
/*
@@ -376,7 +397,7 @@ int mingw_rmdir(const char *pathname)
376397
return -1;
377398
}
378399

379-
if (xutftowcs_path(wpathname, pathname) < 0)
400+
if (xutftowcs_long_path(wpathname, pathname) < 0)
380401
return -1;
381402

382403
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
@@ -455,15 +476,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
455476
int mingw_mkdir(const char *path, int mode)
456477
{
457478
int ret;
458-
wchar_t wpath[MAX_PATH];
479+
wchar_t wpath[MAX_LONG_PATH];
459480

460481
if (!is_valid_win32_path(path, 0)) {
461482
errno = EINVAL;
462483
return -1;
463484
}
464485

465-
if (xutftowcs_path(wpath, path) < 0)
486+
/* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
487+
if (xutftowcs_path_ex(wpath, path, MAX_LONG_PATH, -1, 248,
488+
are_long_paths_enabled()) < 0)
466489
return -1;
490+
467491
ret = _wmkdir(wpath);
468492
if (!ret && needs_hiding(path))
469493
return set_hidden_flag(wpath, 1);
@@ -550,7 +574,7 @@ int mingw_open (const char *filename, int oflags, ...)
550574
va_list args;
551575
unsigned mode;
552576
int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
553-
wchar_t wfilename[MAX_PATH];
577+
wchar_t wfilename[MAX_LONG_PATH];
554578
open_fn_t open_fn;
555579

556580
va_start(args, oflags);
@@ -578,7 +602,7 @@ int mingw_open (const char *filename, int oflags, ...)
578602

579603
if (filename && !strcmp(filename, "/dev/null"))
580604
wcscpy(wfilename, L"nul");
581-
else if (xutftowcs_path(wfilename, filename) < 0)
605+
else if (xutftowcs_long_path(wfilename, filename) < 0)
582606
return -1;
583607

584608
fd = open_fn(wfilename, oflags, mode);
@@ -636,14 +660,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
636660
{
637661
int hide = needs_hiding(filename);
638662
FILE *file;
639-
wchar_t wfilename[MAX_PATH], wotype[4];
663+
wchar_t wfilename[MAX_LONG_PATH], wotype[4];
640664
if (filename && !strcmp(filename, "/dev/null"))
641665
wcscpy(wfilename, L"nul");
642666
else if (!is_valid_win32_path(filename, 1)) {
643667
int create = otype && strchr(otype, 'w');
644668
errno = create ? EINVAL : ENOENT;
645669
return NULL;
646-
} else if (xutftowcs_path(wfilename, filename) < 0)
670+
} else if (xutftowcs_long_path(wfilename, filename) < 0)
647671
return NULL;
648672

649673
if (xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
@@ -665,14 +689,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
665689
{
666690
int hide = needs_hiding(filename);
667691
FILE *file;
668-
wchar_t wfilename[MAX_PATH], wotype[4];
692+
wchar_t wfilename[MAX_LONG_PATH], wotype[4];
669693
if (filename && !strcmp(filename, "/dev/null"))
670694
wcscpy(wfilename, L"nul");
671695
else if (!is_valid_win32_path(filename, 1)) {
672696
int create = otype && strchr(otype, 'w');
673697
errno = create ? EINVAL : ENOENT;
674698
return NULL;
675-
} else if (xutftowcs_path(wfilename, filename) < 0)
699+
} else if (xutftowcs_long_path(wfilename, filename) < 0)
676700
return NULL;
677701

678702
if (xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
@@ -747,27 +771,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
747771

748772
int mingw_access(const char *filename, int mode)
749773
{
750-
wchar_t wfilename[MAX_PATH];
774+
wchar_t wfilename[MAX_LONG_PATH];
751775
if (!strcmp("nul", filename) || !strcmp("/dev/null", filename))
752776
return 0;
753-
if (xutftowcs_path(wfilename, filename) < 0)
777+
if (xutftowcs_long_path(wfilename, filename) < 0)
754778
return -1;
755779
/* X_OK is not supported by the MSVCRT version */
756780
return _waccess(wfilename, mode & ~X_OK);
757781
}
758782

783+
/* cached length of current directory for handle_long_path */
784+
static int current_directory_len = 0;
785+
759786
int mingw_chdir(const char *dirname)
760787
{
761-
wchar_t wdirname[MAX_PATH];
762-
if (xutftowcs_path(wdirname, dirname) < 0)
788+
int result;
789+
wchar_t wdirname[MAX_LONG_PATH];
790+
if (xutftowcs_long_path(wdirname, dirname) < 0)
763791
return -1;
764-
return _wchdir(wdirname);
792+
result = _wchdir(wdirname);
793+
current_directory_len = GetCurrentDirectoryW(0, NULL);
794+
return result;
765795
}
766796

767797
int mingw_chmod(const char *filename, int mode)
768798
{
769-
wchar_t wfilename[MAX_PATH];
770-
if (xutftowcs_path(wfilename, filename) < 0)
799+
wchar_t wfilename[MAX_LONG_PATH];
800+
if (xutftowcs_long_path(wfilename, filename) < 0)
771801
return -1;
772802
return _wchmod(wfilename, mode);
773803
}
@@ -815,8 +845,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
815845
static int do_lstat(int follow, const char *file_name, struct stat *buf)
816846
{
817847
WIN32_FILE_ATTRIBUTE_DATA fdata;
818-
wchar_t wfilename[MAX_PATH];
819-
if (xutftowcs_path(wfilename, file_name) < 0)
848+
wchar_t wfilename[MAX_LONG_PATH];
849+
if (xutftowcs_long_path(wfilename, file_name) < 0)
820850
return -1;
821851

822852
if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
@@ -887,7 +917,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
887917
static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
888918
{
889919
int namelen;
890-
char alt_name[PATH_MAX];
920+
char alt_name[MAX_LONG_PATH];
891921

892922
if (!do_lstat(follow, file_name, buf))
893923
return 0;
@@ -903,7 +933,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
903933
return -1;
904934
while (namelen && file_name[namelen-1] == '/')
905935
--namelen;
906-
if (!namelen || namelen >= PATH_MAX)
936+
if (!namelen || namelen >= MAX_LONG_PATH)
907937
return -1;
908938

909939
memcpy(alt_name, file_name, namelen);
@@ -987,10 +1017,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
9871017
FILETIME mft, aft;
9881018
int rc;
9891019
DWORD attrs;
990-
wchar_t wfilename[MAX_PATH];
1020+
wchar_t wfilename[MAX_LONG_PATH];
9911021
HANDLE osfilehandle;
9921022

993-
if (xutftowcs_path(wfilename, file_name) < 0)
1023+
if (xutftowcs_long_path(wfilename, file_name) < 0)
9941024
return -1;
9951025

9961026
/* must have write permission */
@@ -1073,6 +1103,7 @@ char *mingw_mktemp(char *template)
10731103
wchar_t wtemplate[MAX_PATH];
10741104
int offset = 0;
10751105

1106+
/* we need to return the path, thus no long paths here! */
10761107
if (xutftowcs_path(wtemplate, template) < 0)
10771108
return NULL;
10781109

@@ -1719,6 +1750,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
17191750

17201751
if (*argv && !strcmp(cmd, *argv))
17211752
wcmd[0] = L'\0';
1753+
/*
1754+
* Paths to executables and to the current directory do not support
1755+
* long paths, therefore we cannot use xutftowcs_long_path() here.
1756+
*/
17221757
else if (xutftowcs_path(wcmd, cmd) < 0)
17231758
return -1;
17241759
if (dir && xutftowcs_path(wdir, dir) < 0)
@@ -2367,8 +2402,9 @@ int mingw_rename(const char *pold, const char *pnew)
23672402
{
23682403
DWORD attrs, gle;
23692404
int tries = 0;
2370-
wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
2371-
if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0)
2405+
wchar_t wpold[MAX_LONG_PATH], wpnew[MAX_LONG_PATH];
2406+
if (xutftowcs_long_path(wpold, pold) < 0 ||
2407+
xutftowcs_long_path(wpnew, pnew) < 0)
23722408
return -1;
23732409

23742410
/*
@@ -2682,9 +2718,9 @@ int mingw_raise(int sig)
26822718

26832719
int link(const char *oldpath, const char *newpath)
26842720
{
2685-
wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH];
2686-
if (xutftowcs_path(woldpath, oldpath) < 0 ||
2687-
xutftowcs_path(wnewpath, newpath) < 0)
2721+
wchar_t woldpath[MAX_LONG_PATH], wnewpath[MAX_LONG_PATH];
2722+
if (xutftowcs_long_path(woldpath, oldpath) < 0 ||
2723+
xutftowcs_long_path(wnewpath, newpath) < 0)
26882724
return -1;
26892725

26902726
if (!CreateHardLinkW(wnewpath, woldpath, NULL)) {
@@ -2752,8 +2788,8 @@ int mingw_is_mount_point(struct strbuf *path)
27522788
{
27532789
WIN32_FIND_DATAW findbuf = { 0 };
27542790
HANDLE handle;
2755-
wchar_t wfilename[MAX_PATH];
2756-
int wlen = xutftowcs_path(wfilename, path->buf);
2791+
wchar_t wfilename[MAX_LONG_PATH];
2792+
int wlen = xutftowcs_long_path(wfilename, path->buf);
27572793
if (wlen < 0)
27582794
die(_("could not get long path for '%s'"), path->buf);
27592795

@@ -2898,9 +2934,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
28982934

28992935
static int is_system32_path(const char *path)
29002936
{
2901-
WCHAR system32[MAX_PATH], wpath[MAX_PATH];
2937+
WCHAR system32[MAX_LONG_PATH], wpath[MAX_LONG_PATH];
29022938

2903-
if (xutftowcs_path(wpath, path) < 0 ||
2939+
if (xutftowcs_long_path(wpath, path) < 0 ||
29042940
!GetSystemDirectoryW(system32, ARRAY_SIZE(system32)) ||
29052941
_wcsicmp(system32, wpath))
29062942
return 0;
@@ -3268,6 +3304,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
32683304
}
32693305
}
32703306

3307+
int handle_long_path(wchar_t *path, int len, int max_path, int expand)
3308+
{
3309+
int result;
3310+
wchar_t buf[MAX_LONG_PATH];
3311+
3312+
/*
3313+
* we don't need special handling if path is relative to the current
3314+
* directory, and current directory + path don't exceed the desired
3315+
* max_path limit. This should cover > 99 % of cases with minimal
3316+
* performance impact (git almost always uses relative paths).
3317+
*/
3318+
if ((len < 2 || (!is_dir_sep(path[0]) && path[1] != ':')) &&
3319+
(current_directory_len + len < max_path))
3320+
return len;
3321+
3322+
/*
3323+
* handle everything else:
3324+
* - absolute paths: "C:\dir\file"
3325+
* - absolute UNC paths: "\\server\share\dir\file"
3326+
* - absolute paths on current drive: "\dir\file"
3327+
* - relative paths on other drive: "X:file"
3328+
* - prefixed paths: "\\?\...", "\\.\..."
3329+
*/
3330+
3331+
/* convert to absolute path using GetFullPathNameW */
3332+
result = GetFullPathNameW(path, MAX_LONG_PATH, buf, NULL);
3333+
if (!result) {
3334+
errno = err_win_to_posix(GetLastError());
3335+
return -1;
3336+
}
3337+
3338+
/*
3339+
* return absolute path if it fits within max_path (even if
3340+
* "cwd + path" doesn't due to '..' components)
3341+
*/
3342+
if (result < max_path) {
3343+
wcscpy(path, buf);
3344+
return result;
3345+
}
3346+
3347+
/* error out if we shouldn't expand the path or buf is too small */
3348+
if (!expand || result >= MAX_LONG_PATH - 6) {
3349+
errno = ENAMETOOLONG;
3350+
return -1;
3351+
}
3352+
3353+
/* prefix full path with "\\?\" or "\\?\UNC\" */
3354+
if (buf[0] == '\\') {
3355+
/* ...unless already prefixed */
3356+
if (buf[1] == '\\' && (buf[2] == '?' || buf[2] == '.'))
3357+
return len;
3358+
3359+
wcscpy(path, L"\\\\?\\UNC\\");
3360+
wcscpy(path + 8, buf + 2);
3361+
return result + 6;
3362+
} else {
3363+
wcscpy(path, L"\\\\?\\");
3364+
wcscpy(path + 4, buf);
3365+
return result + 4;
3366+
}
3367+
}
3368+
32713369
#if !defined(_MSC_VER)
32723370
/*
32733371
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -3429,6 +3527,9 @@ int wmain(int argc, const wchar_t **wargv)
34293527
/* initialize Unicode console */
34303528
winansi_init();
34313529

3530+
/* init length of current directory for handle_long_path */
3531+
current_directory_len = GetCurrentDirectoryW(0, NULL);
3532+
34323533
/* invoke the real main() using our utf8 version of argv. */
34333534
exit_status = main(argc, argv);
34343535

0 commit comments

Comments
 (0)