From faacb80440afdc737669dcc88bbac0d4c49240d0 Mon Sep 17 00:00:00 2001
From: Jeff King <peff@peff.net>
Date: Wed, 2 May 2018 15:23:45 -0400
Subject: is_hfs_dotgit: match other .git files

commit 0fc333ba20b43a8afee5023e92cb3384ff4e59a6 upstream.

Both verify_path() and fsck match ".git", ".GIT", and other
variants specific to HFS+. Let's allow matching other
special files like ".gitmodules", which we'll later use to
enforce extra restrictions via verify_path() and fsck.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 utf8.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++------------
 utf8.h |  5 +++++
 2 files changed, 51 insertions(+), 12 deletions(-)

diff --git a/utf8.c b/utf8.c
index 00e10c86ad..0d406b12de 100644
--- a/utf8.c
+++ b/utf8.c
@@ -605,28 +605,33 @@ static ucs_char_t next_hfs_char(const char **in)
 	}
 }
 
-int is_hfs_dotgit(const char *path)
+static int is_hfs_dot_generic(const char *path,
+			      const char *needle, size_t needle_len)
 {
 	ucs_char_t c;
 
 	c = next_hfs_char(&path);
 	if (c != '.')
 		return 0;
-	c = next_hfs_char(&path);
 
 	/*
 	 * there's a great deal of other case-folding that occurs
-	 * in HFS+, but this is enough to catch anything that will
-	 * convert to ".git"
+	 * in HFS+, but this is enough to catch our fairly vanilla
+	 * hard-coded needles.
 	 */
-	if (c != 'g' && c != 'G')
-		return 0;
-	c = next_hfs_char(&path);
-	if (c != 'i' && c != 'I')
-		return 0;
-	c = next_hfs_char(&path);
-	if (c != 't' && c != 'T')
-		return 0;
+	for (; needle_len > 0; needle++, needle_len--) {
+		c = next_hfs_char(&path);
+
+		/*
+		 * We know our needles contain only ASCII, so we clamp here to
+		 * make the results of tolower() sane.
+		 */
+		if (c > 127)
+			return 0;
+		if (tolower(c) != *needle)
+			return 0;
+	}
+
 	c = next_hfs_char(&path);
 	if (c && !is_dir_sep(c))
 		return 0;
@@ -634,6 +639,35 @@ int is_hfs_dotgit(const char *path)
 	return 1;
 }
 
+/*
+ * Inline wrapper to make sure the compiler resolves strlen() on literals at
+ * compile time.
+ */
+static inline int is_hfs_dot_str(const char *path, const char *needle)
+{
+	return is_hfs_dot_generic(path, needle, strlen(needle));
+}
+
+int is_hfs_dotgit(const char *path)
+{
+	return is_hfs_dot_str(path, "git");
+}
+
+int is_hfs_dotgitmodules(const char *path)
+{
+	return is_hfs_dot_str(path, "gitmodules");
+}
+
+int is_hfs_dotgitignore(const char *path)
+{
+	return is_hfs_dot_str(path, "gitignore");
+}
+
+int is_hfs_dotgitattributes(const char *path)
+{
+	return is_hfs_dot_str(path, "gitattributes");
+}
+
 const char utf8_bom[] = "\357\273\277";
 
 int skip_utf8_bom(char **text, size_t len)
diff --git a/utf8.h b/utf8.h
index 6bbcf31a83..da19b43114 100644
--- a/utf8.h
+++ b/utf8.h
@@ -52,8 +52,13 @@ int mbs_chrlen(const char **text, size_t *remainder_p, const char *encoding);
  * The path should be NUL-terminated, but we will match variants of both ".git\0"
  * and ".git/..." (but _not_ ".../.git"). This makes it suitable for both fsck
  * and verify_path().
+ *
+ * Likewise, the is_hfs_dotgitfoo() variants look for ".gitfoo".
  */
 int is_hfs_dotgit(const char *path);
+int is_hfs_dotgitmodules(const char *path);
+int is_hfs_dotgitignore(const char *path);
+int is_hfs_dotgitattributes(const char *path);
 
 typedef enum {
 	ALIGN_LEFT,
-- 
2.17.0.921.gf22659ad46

