Commit ebacd24c6039c992ef9122a0d6f7de0ca4ba3bd1

Edward Thomson 2021-11-01T13:58:18

fs_path: add long path validation on windows

diff --git a/src/fs_path.c b/src/fs_path.c
index 483b21c..de3b039 100644
--- a/src/fs_path.c
+++ b/src/fs_path.c
@@ -1634,11 +1634,25 @@ static bool validate_component(
 	return true;
 }
 
+#ifdef GIT_WIN32
+GIT_INLINE(bool) validate_length(
+	const char *path,
+	size_t len,
+	size_t utf8_char_len)
+{
+	GIT_UNUSED(path);
+	GIT_UNUSED(len);
+
+	return (utf8_char_len <= MAX_PATH);
+}
+#endif
+
 bool git_fs_path_is_valid_str_ext(
 	const git_str *path,
 	unsigned int flags,
 	bool (*validate_char_cb)(char ch, void *payload),
 	bool (*validate_component_cb)(const char *component, size_t len, void *payload),
+	bool (*validate_length_cb)(const char *path, size_t len, size_t utf8_char_len),
 	void *payload)
 {
 	const char *start, *c;
@@ -1683,6 +1697,21 @@ bool git_fs_path_is_valid_str_ext(
 	    !validate_component_cb(start, (c - start), payload))
 		return false;
 
+#ifdef GIT_WIN32
+	if ((flags & GIT_FS_PATH_REJECT_LONG_PATHS) != 0) {
+		size_t utf8_len = git_utf8_char_length(path->ptr, len);
+
+		if (!validate_length(path->ptr, len, utf8_len))
+			return false;
+
+		if (validate_length_cb &&
+		    !validate_length_cb(path->ptr, len, utf8_len))
+			return false;
+	}
+#else
+	GIT_UNUSED(validate_length_cb);
+#endif
+
 	return true;
 }
 
diff --git a/src/fs_path.h b/src/fs_path.h
index 275b3d8..40b4342 100644
--- a/src/fs_path.h
+++ b/src/fs_path.h
@@ -600,8 +600,9 @@ extern int git_fs_path_from_url_or_path(git_str *local_path_out, const char *url
 #define GIT_FS_PATH_REJECT_TRAILING_COLON     (1 << 6)
 #define GIT_FS_PATH_REJECT_DOS_PATHS          (1 << 7)
 #define GIT_FS_PATH_REJECT_NT_CHARS           (1 << 8)
+#define GIT_FS_PATH_REJECT_LONG_PATHS         (1 << 9)
 
-#define GIT_FS_PATH_REJECT_MAX                (1 << 8)
+#define GIT_FS_PATH_REJECT_MAX                (1 << 9)
 
 /* Default path safety for writing files to disk: since we use the
  * Win32 "File Namespace" APIs ("\\?\") we need to protect from
@@ -632,6 +633,7 @@ extern bool git_fs_path_is_valid_str_ext(
 	unsigned int flags,
 	bool (*validate_char_cb)(char ch, void *payload),
 	bool (*validate_component_cb)(const char *component, size_t len, void *payload),
+	bool (*validate_length_cb)(const char *component, size_t len, size_t utf8_char_len),
 	void *payload);
 
 GIT_INLINE(bool) git_fs_path_is_valid_ext(
@@ -639,6 +641,7 @@ GIT_INLINE(bool) git_fs_path_is_valid_ext(
 	unsigned int flags,
 	bool (*validate_char_cb)(char ch, void *payload),
 	bool (*validate_component_cb)(const char *component, size_t len, void *payload),
+	bool (*validate_length_cb)(const char *component, size_t len, size_t utf8_char_len),
 	void *payload)
 {
 	const git_str str = GIT_STR_INIT_CONST(path, SIZE_MAX);
@@ -647,6 +650,7 @@ GIT_INLINE(bool) git_fs_path_is_valid_ext(
 		flags,
 		validate_char_cb,
 		validate_component_cb,
+		validate_length_cb,
 		payload);
 }
 
@@ -662,7 +666,7 @@ GIT_INLINE(bool) git_fs_path_is_valid(
 	unsigned int flags)
 {
 	const git_str str = GIT_STR_INIT_CONST(path, SIZE_MAX);
-	return git_fs_path_is_valid_str_ext(&str, flags, NULL, NULL, NULL);
+	return git_fs_path_is_valid_str_ext(&str, flags, NULL, NULL, NULL, NULL);
 }
 
 /** Validate a filesystem path in a `git_str`. */
@@ -670,7 +674,7 @@ GIT_INLINE(bool) git_fs_path_is_valid_str(
 	const git_str *path,
 	unsigned int flags)
 {
-	return git_fs_path_is_valid_str_ext(path, flags, NULL, NULL, NULL);
+	return git_fs_path_is_valid_str_ext(path, flags, NULL, NULL, NULL, NULL);
 }
 
 /**
diff --git a/src/path.c b/src/path.c
index d54bc5b..a6b396f 100644
--- a/src/path.c
+++ b/src/path.c
@@ -301,7 +301,7 @@ bool git_path_is_valid(
 	data.file_mode = file_mode;
 	data.flags = flags;
 
-	return git_fs_path_is_valid_ext(path, flags, NULL, validate_repo_component, &data);
+	return git_fs_path_is_valid_ext(path, flags, NULL, validate_repo_component, NULL, &data);
 }
 
 static const struct {