Skip to content

Commit 5148aef

Browse files
committed
Merge branch 'jeffhostetler/jeffhostetler/fscache_nfd'
Signed-off-by: Johannes Schindelin <[email protected]>
2 parents f412361 + e831604 commit 5148aef

File tree

8 files changed

+470
-53
lines changed

8 files changed

+470
-53
lines changed

Documentation/config/core.txt

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

560+
core.longpaths::
561+
Enable long path (> 260) support for builtin commands in Git for
562+
Windows. This is disabled by default, as long paths are not supported
563+
by Windows Explorer, cmd.exe and the Git for Windows tool chain
564+
(msys, bash, tcl, perl...). Only enable this if you know what you're
565+
doing and are prepared to live with a few quirks.
566+
560567
core.unsetenvvars::
561568
Windows-only: comma-separated list of environment variables'
562569
names that need to be unset before spawning any other process.

compat/mingw.c

Lines changed: 114 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ enum hide_dotfiles_type {
228228
static enum hide_dotfiles_type hide_dotfiles = HIDE_DOTFILES_DOTGITONLY;
229229
static char *unset_environment_variables;
230230
int core_fscache;
231+
int core_long_paths;
231232

232233
int mingw_core_config(const char *var, const char *value, void *cb)
233234
{
@@ -244,6 +245,11 @@ int mingw_core_config(const char *var, const char *value, void *cb)
244245
return 0;
245246
}
246247

248+
if (!strcmp(var, "core.longpaths")) {
249+
core_long_paths = git_config_bool(var, value);
250+
return 0;
251+
}
252+
247253
if (!strcmp(var, "core.unsetenvvars")) {
248254
free(unset_environment_variables);
249255
unset_environment_variables = xstrdup(value);
@@ -281,8 +287,8 @@ static wchar_t *normalize_ntpath(wchar_t *wbuf)
281287
int mingw_unlink(const char *pathname)
282288
{
283289
int ret, tries = 0;
284-
wchar_t wpathname[MAX_PATH];
285-
if (xutftowcs_path(wpathname, pathname) < 0)
290+
wchar_t wpathname[MAX_LONG_PATH];
291+
if (xutftowcs_long_path(wpathname, pathname) < 0)
286292
return -1;
287293

288294
/* read-only files cannot be removed */
@@ -311,7 +317,7 @@ static int is_dir_empty(const wchar_t *wpath)
311317
{
312318
WIN32_FIND_DATAW findbuf;
313319
HANDLE handle;
314-
wchar_t wbuf[MAX_PATH + 2];
320+
wchar_t wbuf[MAX_LONG_PATH + 2];
315321
wcscpy(wbuf, wpath);
316322
wcscat(wbuf, L"\\*");
317323
handle = FindFirstFileW(wbuf, &findbuf);
@@ -332,8 +338,8 @@ static int is_dir_empty(const wchar_t *wpath)
332338
int mingw_rmdir(const char *pathname)
333339
{
334340
int ret, tries = 0;
335-
wchar_t wpathname[MAX_PATH];
336-
if (xutftowcs_path(wpathname, pathname) < 0)
341+
wchar_t wpathname[MAX_LONG_PATH];
342+
if (xutftowcs_long_path(wpathname, pathname) < 0)
337343
return -1;
338344

339345
while ((ret = _wrmdir(wpathname)) == -1 && tries < ARRAY_SIZE(delay)) {
@@ -408,9 +414,12 @@ static int set_hidden_flag(const wchar_t *path, int set)
408414
int mingw_mkdir(const char *path, int mode)
409415
{
410416
int ret;
411-
wchar_t wpath[MAX_PATH];
412-
if (xutftowcs_path(wpath, path) < 0)
417+
wchar_t wpath[MAX_LONG_PATH];
418+
/* CreateDirectoryW path limit is 248 (MAX_PATH - 8.3 file name) */
419+
if (xutftowcs_path_ex(wpath, path, MAX_LONG_PATH, -1, 248,
420+
core_long_paths) < 0)
413421
return -1;
422+
414423
ret = _wmkdir(wpath);
415424
if (!ret && needs_hiding(path))
416425
return set_hidden_flag(wpath, 1);
@@ -483,7 +492,7 @@ int mingw_open (const char *filename, int oflags, ...)
483492
va_list args;
484493
unsigned mode;
485494
int fd;
486-
wchar_t wfilename[MAX_PATH];
495+
wchar_t wfilename[MAX_LONG_PATH];
487496
open_fn_t open_fn;
488497

489498
va_start(args, oflags);
@@ -498,7 +507,7 @@ int mingw_open (const char *filename, int oflags, ...)
498507
else
499508
open_fn = _wopen;
500509

501-
if (xutftowcs_path(wfilename, filename) < 0)
510+
if (xutftowcs_long_path(wfilename, filename) < 0)
502511
return -1;
503512
fd = open_fn(wfilename, oflags, mode);
504513

@@ -555,10 +564,10 @@ FILE *mingw_fopen (const char *filename, const char *otype)
555564
{
556565
int hide = needs_hiding(filename);
557566
FILE *file;
558-
wchar_t wfilename[MAX_PATH], wotype[4];
567+
wchar_t wfilename[MAX_LONG_PATH], wotype[4];
559568
if (filename && !strcmp(filename, "/dev/null"))
560569
filename = "nul";
561-
if (xutftowcs_path(wfilename, filename) < 0 ||
570+
if (xutftowcs_long_path(wfilename, filename) < 0 ||
562571
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
563572
return NULL;
564573
if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
@@ -577,10 +586,10 @@ FILE *mingw_freopen (const char *filename, const char *otype, FILE *stream)
577586
{
578587
int hide = needs_hiding(filename);
579588
FILE *file;
580-
wchar_t wfilename[MAX_PATH], wotype[4];
589+
wchar_t wfilename[MAX_LONG_PATH], wotype[4];
581590
if (filename && !strcmp(filename, "/dev/null"))
582591
filename = "nul";
583-
if (xutftowcs_path(wfilename, filename) < 0 ||
592+
if (xutftowcs_long_path(wfilename, filename) < 0 ||
584593
xutftowcs(wotype, otype, ARRAY_SIZE(wotype)) < 0)
585594
return NULL;
586595
if (hide && !access(filename, F_OK) && set_hidden_flag(wfilename, 0)) {
@@ -634,25 +643,31 @@ ssize_t mingw_write(int fd, const void *buf, size_t len)
634643

635644
int mingw_access(const char *filename, int mode)
636645
{
637-
wchar_t wfilename[MAX_PATH];
638-
if (xutftowcs_path(wfilename, filename) < 0)
646+
wchar_t wfilename[MAX_LONG_PATH];
647+
if (xutftowcs_long_path(wfilename, filename) < 0)
639648
return -1;
640649
/* X_OK is not supported by the MSVCRT version */
641650
return _waccess(wfilename, mode & ~X_OK);
642651
}
643652

653+
/* cached length of current directory for handle_long_path */
654+
static int current_directory_len = 0;
655+
644656
int mingw_chdir(const char *dirname)
645657
{
646-
wchar_t wdirname[MAX_PATH];
647-
if (xutftowcs_path(wdirname, dirname) < 0)
658+
int result;
659+
wchar_t wdirname[MAX_LONG_PATH];
660+
if (xutftowcs_long_path(wdirname, dirname) < 0)
648661
return -1;
649-
return _wchdir(wdirname);
662+
result = _wchdir(wdirname);
663+
current_directory_len = GetCurrentDirectoryW(0, NULL);
664+
return result;
650665
}
651666

652667
int mingw_chmod(const char *filename, int mode)
653668
{
654-
wchar_t wfilename[MAX_PATH];
655-
if (xutftowcs_path(wfilename, filename) < 0)
669+
wchar_t wfilename[MAX_LONG_PATH];
670+
if (xutftowcs_long_path(wfilename, filename) < 0)
656671
return -1;
657672
return _wchmod(wfilename, mode);
658673
}
@@ -700,8 +715,8 @@ static int has_valid_directory_prefix(wchar_t *wfilename)
700715
static int do_lstat(int follow, const char *file_name, struct stat *buf)
701716
{
702717
WIN32_FILE_ATTRIBUTE_DATA fdata;
703-
wchar_t wfilename[MAX_PATH];
704-
if (xutftowcs_path(wfilename, file_name) < 0)
718+
wchar_t wfilename[MAX_LONG_PATH];
719+
if (xutftowcs_long_path(wfilename, file_name) < 0)
705720
return -1;
706721

707722
if (GetFileAttributesExW(wfilename, GetFileExInfoStandard, &fdata)) {
@@ -772,7 +787,7 @@ static int do_lstat(int follow, const char *file_name, struct stat *buf)
772787
static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
773788
{
774789
int namelen;
775-
char alt_name[PATH_MAX];
790+
char alt_name[MAX_LONG_PATH];
776791

777792
if (!do_lstat(follow, file_name, buf))
778793
return 0;
@@ -788,7 +803,7 @@ static int do_stat_internal(int follow, const char *file_name, struct stat *buf)
788803
return -1;
789804
while (namelen && file_name[namelen-1] == '/')
790805
--namelen;
791-
if (!namelen || namelen >= PATH_MAX)
806+
if (!namelen || namelen >= MAX_LONG_PATH)
792807
return -1;
793808

794809
memcpy(alt_name, file_name, namelen);
@@ -872,8 +887,8 @@ int mingw_utime (const char *file_name, const struct utimbuf *times)
872887
FILETIME mft, aft;
873888
int fh, rc;
874889
DWORD attrs;
875-
wchar_t wfilename[MAX_PATH];
876-
if (xutftowcs_path(wfilename, file_name) < 0)
890+
wchar_t wfilename[MAX_LONG_PATH];
891+
if (xutftowcs_long_path(wfilename, file_name) < 0)
877892
return -1;
878893

879894
/* must have write permission */
@@ -934,6 +949,7 @@ char *mingw_mktemp(char *template)
934949
wchar_t wtemplate[MAX_PATH];
935950
int offset = 0;
936951

952+
/* we need to return the path, thus no long paths here! */
937953
if (xutftowcs_path(wtemplate, template) < 0)
938954
return NULL;
939955

@@ -1455,6 +1471,7 @@ static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **deltaen
14551471
si.hStdOutput = winansi_get_osfhandle(fhout);
14561472
si.hStdError = winansi_get_osfhandle(fherr);
14571473

1474+
/* executables and the current directory don't support long paths */
14581475
if (xutftowcs_path(wcmd, cmd) < 0)
14591476
return -1;
14601477
if (dir && xutftowcs_path(wdir, dir) < 0)
@@ -1851,8 +1868,9 @@ int mingw_rename(const char *pold, const char *pnew)
18511868
{
18521869
DWORD attrs, gle;
18531870
int tries = 0;
1854-
wchar_t wpold[MAX_PATH], wpnew[MAX_PATH];
1855-
if (xutftowcs_path(wpold, pold) < 0 || xutftowcs_path(wpnew, pnew) < 0)
1871+
wchar_t wpold[MAX_LONG_PATH], wpnew[MAX_LONG_PATH];
1872+
if (xutftowcs_long_path(wpold, pold) < 0 ||
1873+
xutftowcs_long_path(wpnew, pnew) < 0)
18561874
return -1;
18571875

18581876
/*
@@ -2161,9 +2179,9 @@ int mingw_raise(int sig)
21612179

21622180
int link(const char *oldpath, const char *newpath)
21632181
{
2164-
wchar_t woldpath[MAX_PATH], wnewpath[MAX_PATH];
2165-
if (xutftowcs_path(woldpath, oldpath) < 0 ||
2166-
xutftowcs_path(wnewpath, newpath) < 0)
2182+
wchar_t woldpath[MAX_LONG_PATH], wnewpath[MAX_LONG_PATH];
2183+
if (xutftowcs_long_path(woldpath, oldpath) < 0 ||
2184+
xutftowcs_long_path(wnewpath, newpath) < 0)
21672185
return -1;
21682186

21692187
if (!CreateHardLinkW(wnewpath, woldpath, NULL)) {
@@ -2339,6 +2357,68 @@ static void setup_windows_environment(void)
23392357
setenv("TERM", "cygwin", 1);
23402358
}
23412359

2360+
int handle_long_path(wchar_t *path, int len, int max_path, int expand)
2361+
{
2362+
int result;
2363+
wchar_t buf[MAX_LONG_PATH];
2364+
2365+
/*
2366+
* we don't need special handling if path is relative to the current
2367+
* directory, and current directory + path don't exceed the desired
2368+
* max_path limit. This should cover > 99 % of cases with minimal
2369+
* performance impact (git almost always uses relative paths).
2370+
*/
2371+
if ((len < 2 || (!is_dir_sep(path[0]) && path[1] != ':')) &&
2372+
(current_directory_len + len < max_path))
2373+
return len;
2374+
2375+
/*
2376+
* handle everything else:
2377+
* - absolute paths: "C:\dir\file"
2378+
* - absolute UNC paths: "\\server\share\dir\file"
2379+
* - absolute paths on current drive: "\dir\file"
2380+
* - relative paths on other drive: "X:file"
2381+
* - prefixed paths: "\\?\...", "\\.\..."
2382+
*/
2383+
2384+
/* convert to absolute path using GetFullPathNameW */
2385+
result = GetFullPathNameW(path, MAX_LONG_PATH, buf, NULL);
2386+
if (!result) {
2387+
errno = err_win_to_posix(GetLastError());
2388+
return -1;
2389+
}
2390+
2391+
/*
2392+
* return absolute path if it fits within max_path (even if
2393+
* "cwd + path" doesn't due to '..' components)
2394+
*/
2395+
if (result < max_path) {
2396+
wcscpy(path, buf);
2397+
return result;
2398+
}
2399+
2400+
/* error out if we shouldn't expand the path or buf is too small */
2401+
if (!expand || result >= MAX_LONG_PATH - 6) {
2402+
errno = ENAMETOOLONG;
2403+
return -1;
2404+
}
2405+
2406+
/* prefix full path with "\\?\" or "\\?\UNC\" */
2407+
if (buf[0] == '\\') {
2408+
/* ...unless already prefixed */
2409+
if (buf[1] == '\\' && (buf[2] == '?' || buf[2] == '.'))
2410+
return len;
2411+
2412+
wcscpy(path, L"\\\\?\\UNC\\");
2413+
wcscpy(path + 8, buf + 2);
2414+
return result + 6;
2415+
} else {
2416+
wcscpy(path, L"\\\\?\\");
2417+
wcscpy(path + 4, buf);
2418+
return result + 4;
2419+
}
2420+
}
2421+
23422422
#if !defined(_MSC_VER)
23432423
/*
23442424
* Disable MSVCRT command line wildcard expansion (__getmainargs called from
@@ -2494,6 +2574,9 @@ int wmain(int argc, const wchar_t **wargv)
24942574
/* initialize Unicode console */
24952575
winansi_init();
24962576

2577+
/* init length of current directory for handle_long_path */
2578+
current_directory_len = GetCurrentDirectoryW(0, NULL);
2579+
24972580
/* invoke the real main() using our utf8 version of argv. */
24982581
exit_status = main(argc, argv);
24992582

0 commit comments

Comments
 (0)