Commit 27187344bb33060ffc4208f7b7f2412fce2b7110

Carlos Martín Nieto 2015-09-23T15:16:01

Merge pull request #3435 from ethomson/nametoolong win32: give better error message on long paths

diff --git a/src/win32/path_w32.c b/src/win32/path_w32.c
index 118e8bc..40b95c3 100644
--- a/src/win32/path_w32.c
+++ b/src/win32/path_w32.c
@@ -198,13 +198,13 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
 	/* See if this is an absolute path (beginning with a drive letter) */
 	if (path__is_absolute(src)) {
 		if (git__utf8_to_16(dest, MAX_PATH, src) < 0)
-			return -1;
+			goto on_error;
 	}
 	/* File-prefixed NT-style paths beginning with \\?\ */
 	else if (path__is_nt_namespace(src)) {
 		/* Skip the NT prefix, the destination already contains it */
 		if (git__utf8_to_16(dest, MAX_PATH, src + PATH__NT_NAMESPACE_LEN) < 0)
-			return -1;
+			goto on_error;
 	}
 	/* UNC paths */
 	else if (path__is_unc(src)) {
@@ -213,36 +213,43 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
 
 		/* Skip the leading "\\" */
 		if (git__utf8_to_16(dest, MAX_PATH - 2, src + 2) < 0)
-			return -1;
+			goto on_error;
 	}
 	/* Absolute paths omitting the drive letter */
 	else if (src[0] == '\\' || src[0] == '/') {
 		if (path__cwd(dest, MAX_PATH) < 0)
-			return -1;
+			goto on_error;
 
 		if (!path__is_absolute(dest)) {
 			errno = ENOENT;
-			return -1;
+			goto on_error;
 		}
 
 		/* Skip the drive letter specification ("C:") */	
 		if (git__utf8_to_16(dest + 2, MAX_PATH - 2, src) < 0)
-			return -1;
+			goto on_error;
 	}
 	/* Relative paths */
 	else {
 		int cwd_len;
 
 		if ((cwd_len = git_win32_path__cwd(dest, MAX_PATH)) < 0)
-			return -1;
+			goto on_error;
 
 		dest[cwd_len++] = L'\\';
 
 		if (git__utf8_to_16(dest + cwd_len, MAX_PATH - cwd_len, src) < 0)
-			return -1;
+			goto on_error;
 	}
 
 	return git_win32_path_canonicalize(out);
+
+on_error:
+	/* set windows error code so we can use its error message */
+	if (errno == ENAMETOOLONG)
+		SetLastError(ERROR_FILENAME_EXCED_RANGE);
+
+	return -1;
 }
 
 int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src)
diff --git a/tests/win32/longpath.c b/tests/win32/longpath.c
new file mode 100644
index 0000000..4fe851c
--- /dev/null
+++ b/tests/win32/longpath.c
@@ -0,0 +1,62 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+#include "clone.h"
+#include "buffer.h"
+#include "fileops.h"
+
+static git_buf path = GIT_BUF_INIT;
+
+void test_win32_longpath__initialize(void)
+{
+#ifdef GIT_WIN32
+	const char *base = clar_sandbox_path();
+	size_t base_len = strlen(base);
+	size_t remain = MAX_PATH - base_len;
+	size_t i;
+
+	git_buf_clear(&path);
+	git_buf_puts(&path, base);
+	git_buf_putc(&path, '/');
+
+	cl_assert(remain < (MAX_PATH - 5));
+
+	for (i = 0; i < (remain - 5); i++)
+		git_buf_putc(&path, 'a');
+
+	printf("%s %" PRIuZ "\n", path.ptr, path.size);
+#endif
+}
+
+void test_win32_longpath__cleanup(void)
+{
+	git_buf_free(&path);
+}
+
+#ifdef GIT_WIN32
+void assert_name_too_long(void)
+{
+	const git_error *err;
+	size_t expected_len, actual_len;
+	const char *expected_msg;
+
+	err = giterr_last();
+	actual_len = strlen(err->message);
+
+	expected_msg = git_win32_get_error_message(ERROR_FILENAME_EXCED_RANGE);
+	expected_len = strlen(expected_msg);
+
+	/* check the suffix */
+	cl_assert_equal_s(expected_msg, err->message + (actual_len - expected_len));
+}
+#endif
+
+void test_win32_longpath__errmsg_on_checkout(void)
+{
+#ifdef GIT_WIN32
+	git_repository *repo;
+
+	cl_git_fail(git_clone(&repo, cl_fixture("testrepo.git"), path.ptr, NULL));
+	assert_name_too_long();
+#endif
+}