Skip to content

Commit 4445300

Browse files
committed
Merge branch 'long-paths'
2 parents 26aceef + 15f2a99 commit 4445300

File tree

7 files changed

+444
-56
lines changed

7 files changed

+444
-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: 137 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
{
@@ -309,8 +330,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
309330
int mingw_unlink(const char *pathname)
310331
{
311332
int ret, tries = 0;
312-
wchar_t wpathname[MAX_PATH];
313-
if (xutftowcs_path(wpathname, pathname) < 0)
333+
wchar_t wpathname[MAX_LONG_PATH];
334+
if (xutftowcs_long_path(wpathname, pathname) < 0)
314335
return -1;
315336

316337
if (DeleteFileW(wpathname))
@@ -342,7 +363,7 @@ static int is_dir_empty(const wchar_t *wpath)
342363
{
343364
WIN32_FIND_DATAW findbuf;
344365
HANDLE handle;
345-
wchar_t wbuf[MAX_PATH + 2];
366+
wchar_t wbuf[MAX_LONG_PATH + 2];
346367
wcscpy(wbuf, wpath);
347368
wcscat(wbuf, L"\\*");
348369
handle = FindFirstFileW(wbuf, &findbuf);
@@ -363,7 +384,7 @@ static int is_dir_empty(const wchar_t *wpath)
363384
int mingw_rmdir(const char *pathname)
364385
{
365386
int ret, tries = 0;
366-
wchar_t wpathname[MAX_PATH];
387+
wchar_t wpathname[MAX_LONG_PATH];
367388
struct stat st;
368389

369390
/*
@@ -385,7 +406,7 @@ int mingw_rmdir(const char *pathname)
385406
return -1;
386407
}
387408

388-
if (xutftowcs_path(wpathname, pathname) < 0)
409+
if (xutftowcs_long_path(wpathname, pathname) < 0)
389410
return -1;
390411

391412
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
@@ -464,15 +485,18 @@ static int set_hidden_flag(const wchar_t *path, int set)
464485
int mingw_mkdir(const char *path, int mode)
465486
{
466487
int ret;
467-
wchar_t wpath[MAX_PATH];
488+
wchar_t wpath[MAX_LONG_PATH];
468489

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

474-
if (xutftowcs_path(wpath, path) < 0)
495+
/* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
496+
if (xutftowcs_path_ex(wpath, path, MAX_LONG_PATH, -1, 248,
497+
are_long_paths_enabled()) < 0)
475498
return -1;
499+
476500
ret = _wmkdir(wpath);
477501
if (!ret && needs_hiding(path))
478502
return set_hidden_flag(wpath, 1);
@@ -559,7 +583,7 @@ int mingw_open (const char *filename, int oflags, ...)
559583
va_list args;
560584
unsigned mode;
561585
int fd, create = (oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL);
562-
wchar_t wfilename[MAX_PATH];
586+
wchar_t wfilename[MAX_LONG_PATH];
563587
open_fn_t open_fn;
564588

565589
va_start(args, oflags);
@@ -587,7 +611,7 @@ int mingw_open (const char *filename, int oflags, ...)
587611

588612
if (filename && !strcmp(filename, "/dev/null"))
589613
wcscpy(wfilename, L"nul");
590-
else if (xutftowcs_path(wfilename, filename) < 0)
614+
else if (xutftowcs_long_path(wfilename, filename) < 0)
591615
return -1;
592616

593617
fd = open_fn(wfilename, oflags, mode);
@@ -645,14 +669,14 @@ FILE *mingw_fopen (const char *filename, const char *otype)
645669
{
646670
int hide = needs_hiding(filename);
647671
FILE *file;
648-
wchar_t wfilename[MAX_PATH], wotype[4];
672+
wchar_t wfilename[MAX_LONG_PATH], wotype[4];
649673
if (filename && !strcmp(filename, "/dev/null"))
650674
wcscpy(wfilename, L"nul");
651675
else if (!is_valid_win32_path(filename, 1)) {
652676
int create = otype && strchr(otype, 'w');
653677
errno = create ? EINVAL : ENOENT;
654678
return NULL;
655-
} else if (xutftowcs_path(wfilename, filename) < 0)
679+
} else if (xutftowcs_long_path(wfilename, filename) < 0)
656680
return NULL;
657681

658682
if (xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
@@ -674,14 +698,14 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
674698
{
675699
int hide = needs_hiding(filename);
676700
FILE *file;
677-
wchar_t wfilename[MAX_PATH], wotype[4];
701+
wchar_t wfilename[MAX_LONG_PATH], wotype[4];
678702
if (filename && !strcmp(filename, "/dev/null"))
679703
wcscpy(wfilename, L"nul");
680704
else if (!is_valid_win32_path(filename, 1)) {
681705
int create = otype && strchr(otype, 'w');
682706
errno = create ? EINVAL : ENOENT;
683707
return NULL;
684-
} else if (xutftowcs_path(wfilename, filename) < 0)
708+
} else if (xutftowcs_long_path(wfilename, filename) < 0)
685709
return NULL;
686710

687711
if (xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
@@ -730,7 +754,7 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
730754
if (GetFileType(h) == FILE_TYPE_PIPE)
731755
errno = EPIPE;
732756
else {
733-
wchar_t path[MAX_PATH];
757+
wchar_t path[MAX_LONG_PATH];
734758
DWORD ret = GetFinalPathNameByHandleW(h, path,
735759
ARRAY_SIZE(path), 0);
736760
UINT drive_type = ret > 0 && ret < ARRAY_SIZE(path) ?
@@ -756,27 +780,33 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
756780

757781
int mingw_access(const char *filename, int mode)
758782
{
759-
wchar_t wfilename[MAX_PATH];
783+
wchar_t wfilename[MAX_LONG_PATH];
760784
if (!strcmp("nul", filename) || !strcmp("/dev/null", filename))
761785
return 0;
762-
if (xutftowcs_path(wfilename, filename) < 0)
786+
if (xutftowcs_long_path(wfilename, filename) < 0)
763787
return -1;
764788
/* X_OK is not supported by the MSVCRT version */
765789
return _waccess(wfilename, mode & ~X_OK);
766790
}
767791

792+
/* cached length of current directory for handle_long_path */
793+
static int current_directory_len = 0;
794+
768795
int mingw_chdir(const char *dirname)
769796
{
770-
wchar_t wdirname[MAX_PATH];
771-
if (xutftowcs_path(wdirname, dirname) < 0)
797+
int result;
798+
wchar_t wdirname[MAX_LONG_PATH];
799+
if (xutftowcs_long_path(wdirname, dirname) < 0)
772800
return -1;
773-
return _wchdir(wdirname);
801+
result = _wchdir(wdirname);
802+
current_directory_len = GetCurrentDirectoryW(0, NULL);
803+
return result;
774804
}
775805

776806
int mingw_chmod(const char *filename, int mode)
777807
{
778-
wchar_t wfilename[MAX_PATH];
779-
if (xutftowcs_path(wfilename, filename) < 0)
808+
wchar_t wfilename[MAX_LONG_PATH];
809+
if (xutftowcs_long_path(wfilename, filename) < 0)
780810
return -1;
781811
return _wchmod(wfilename, mode);
782812
}
@@ -824,8 +854,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
824854
static int do_lstat(int follow, const char *file_name, struct stat *buf)
825855
{
826856
WIN32_FILE_ATTRIBUTE_DATA fdata;
827-
wchar_t wfilename[MAX_PATH];
828-
if (xutftowcs_path(wfilename, file_name) < 0)
857+
wchar_t wfilename[MAX_LONG_PATH];
858+
if (xutftowcs_long_path(wfilename, file_name) < 0)
829859
return -1;
830860

831861
if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
@@ -896,7 +926,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
896926
static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
897927
{
898928
int namelen;
899-
char alt_name[PATH_MAX];
929+
char alt_name[MAX_LONG_PATH];
900930

901931
if (!do_lstat(follow, file_name, buf))
902932
return 0;
@@ -912,7 +942,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
912942
return -1;
913943
while (namelen && file_name[namelen-1] == '/')
914944
--namelen;
915-
if (!namelen || namelen >= PATH_MAX)
945+
if (!namelen || namelen >= MAX_LONG_PATH)
916946
return -1;
917947

918948
memcpy(alt_name, file_name, namelen);
@@ -996,10 +1026,10 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
9961026
FILETIME mft, aft;
9971027
int rc;
9981028
DWORD attrs;
999-
wchar_t wfilename[MAX_PATH];
1029+
wchar_t wfilename[MAX_LONG_PATH];
10001030
HANDLE osfilehandle;
10011031

1002-
if (xutftowcs_path(wfilename, file_name) < 0)
1032+
if (xutftowcs_long_path(wfilename, file_name) < 0)
10031033
return -1;
10041034

10051035
/* must have write permission */
@@ -1082,6 +1112,7 @@ char *mingw_mktemp(char *template)
10821112
wchar_t wtemplate[MAX_PATH];
10831113
int offset = 0;
10841114

1115+
/* we need to return the path, thus no long paths here! */
10851116
if (xutftowcs_path(wtemplate, template) < 0)
10861117
return NULL;
10871118

@@ -1733,6 +1764,10 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
17331764

17341765
if (*argv && !strcmp(cmd, *argv))
17351766
wcmd[0] = L'\0';
1767+
/*
1768+
* Paths to executables and to the current directory do not support
1769+
* long paths, therefore we cannot use xutftowcs_long_path() here.
1770+
*/
17361771
else if (xutftowcs_path(wcmd, cmd) < 0)
17371772
return -1;
17381773
if (dir && xutftowcs_path(wdir, dir) < 0)
@@ -2381,8 +2416,9 @@ int mingw_rename(const char *pold, const char *pnew)
23812416
{
23822417
DWORD attrs, gle;
23832418
int tries = 0;
2384-
wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
2385-
if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0)
2419+
wchar_t wpold[MAX_LONG_PATH], wpnew[MAX_LONG_PATH];
2420+
if (xutftowcs_long_path(wpold, pold) < 0 ||
2421+
xutftowcs_long_path(wpnew, pnew) < 0)
23862422
return -1;
23872423

23882424
/*
@@ -2696,9 +2732,9 @@ int mingw_raise(int sig)
26962732

26972733
int link(const char *oldpath, const char *newpath)
26982734
{
2699-
wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH];
2700-
if (xutftowcs_path(woldpath, oldpath) < 0 ||
2701-
xutftowcs_path(wnewpath, newpath) < 0)
2735+
wchar_t woldpath[MAX_LONG_PATH], wnewpath[MAX_LONG_PATH];
2736+
if (xutftowcs_long_path(woldpath, oldpath) < 0 ||
2737+
xutftowcs_long_path(wnewpath, newpath) < 0)
27022738
return -1;
27032739

27042740
if (!CreateHardLinkW(wnewpath, woldpath, NULL)) {
@@ -2766,8 +2802,8 @@ int mingw_is_mount_point(struct strbuf *path)
27662802
{
27672803
WIN32_FIND_DATAW findbuf = { 0 };
27682804
HANDLE handle;
2769-
wchar_t wfilename[MAX_PATH];
2770-
int wlen = xutftowcs_path(wfilename, path->buf);
2805+
wchar_t wfilename[MAX_LONG_PATH];
2806+
int wlen = xutftowcs_long_path(wfilename, path->buf);
27712807
if (wlen < 0)
27722808
die(_("could not get long path for '%s'"), path->buf);
27732809

@@ -2912,9 +2948,9 @@ static size_t append_system_bin_dirs(char *path, size_t size)
29122948

29132949
static int is_system32_path(const char *path)
29142950
{
2915-
WCHAR system32[MAX_PATH], wpath[MAX_PATH];
2951+
WCHAR system32[MAX_LONG_PATH], wpath[MAX_LONG_PATH];
29162952

2917-
if (xutftowcs_path(wpath, path) < 0 ||
2953+
if (xutftowcs_long_path(wpath, path) < 0 ||
29182954
!GetSystemDirectoryW(system32, ARRAY_SIZE(system32)) ||
29192955
_wcsicmp(system32, wpath))
29202956
return 0;
@@ -3282,6 +3318,68 @@ int is_valid_win32_path(const char *path, int allow_literal_nul)
32823318
}
32833319
}
32843320

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

3544+
/* init length of current directory for handle_long_path */
3545+
current_directory_len = GetCurrentDirectoryW(0, NULL);
3546+
34463547
/* invoke the real main() using our utf8 version of argv. */
34473548
exit_status = main(argc, argv);
34483549

0 commit comments

Comments
 (0)