summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/path.c73
-rw-r--r--src/repository.h2
2 files changed, 53 insertions, 22 deletions
diff --git a/src/path.c b/src/path.c
index 32cae8e5a..6d37abda1 100644
--- a/src/path.c
+++ b/src/path.c
@@ -21,7 +21,29 @@
#include <stdio.h>
#include <ctype.h>
-#define LOOKS_LIKE_DRIVE_PREFIX(S) (git__isalpha((S)[0]) && (S)[1] == ':')
+static int dos_drive_prefix_length(const char *path)
+{
+ int i;
+
+ /*
+ * Does it start with an ASCII letter (i.e. highest bit not set),
+ * followed by a colon?
+ */
+ if (!(0x80 & (unsigned char)*path))
+ return *path && path[1] == ':' ? 2 : 0;
+
+ /*
+ * While drive letters must be letters of the English alphabet, it is
+ * possible to assign virtually _any_ Unicode character via `subst` as
+ * a drive letter to "virtual drives". Even `1`, or `ä`. Or fun stuff
+ * like this:
+ *
+ * subst ֍: %USERPROFILE%\Desktop
+ */
+ for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++)
+ ; /* skip first UTF-8 character */
+ return path[i] == ':' ? i + 1 : 0;
+}
#ifdef GIT_WIN32
static bool looks_like_network_computer_name(const char *path, int pos)
@@ -123,11 +145,11 @@ static int win32_prefix_length(const char *path, int len)
GIT_UNUSED(len);
#else
/*
- * Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return
- * 'C:/' here
+ * Mimic unix behavior where '/.git' returns '/': 'C:/.git'
+ * will return 'C:/' here
*/
- if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path))
- return 2;
+ if (dos_drive_prefix_length(path) == len)
+ return len;
/*
* Similarly checks if we're dealing with a network computer name
@@ -260,11 +282,11 @@ const char *git_path_topdir(const char *path)
int git_path_root(const char *path)
{
- int offset = 0;
+ int offset = 0, prefix_len;
/* Does the root of the path look like a windows drive ? */
- if (LOOKS_LIKE_DRIVE_PREFIX(path))
- offset += 2;
+ if ((prefix_len = dos_drive_prefix_length(path)))
+ offset += prefix_len;
#ifdef GIT_WIN32
/* Are we dealing with a windows network path? */
@@ -1609,8 +1631,12 @@ GIT_INLINE(bool) verify_dotgit_ntfs(git_repository *repo, const char *path, size
if (!start)
return true;
- /* Reject paths like ".git\" */
- if (path[start] == '\\')
+ /*
+ * Reject paths that start with Windows-style directory separators
+ * (".git\") or NTFS alternate streams (".git:") and could be used
+ * to write to the ".git" directory on Windows platforms.
+ */
+ if (path[start] == '\\' || path[start] == ':')
return false;
/* Reject paths like '.git ' or '.git.' */
@@ -1622,12 +1648,21 @@ GIT_INLINE(bool) verify_dotgit_ntfs(git_repository *repo, const char *path, size
return false;
}
-GIT_INLINE(bool) only_spaces_and_dots(const char *path)
+/*
+ * Windows paths that end with spaces and/or dots are elided to the
+ * path without them for backward compatibility. That is to say
+ * that opening file "foo ", "foo." or even "foo . . ." will all
+ * map to a filename of "foo". This function identifies spaces and
+ * dots at the end of a filename, whether the proper end of the
+ * filename (end of string) or a colon (which would indicate a
+ * Windows alternate data stream.)
+ */
+GIT_INLINE(bool) ntfs_end_of_filename(const char *path)
{
const char *c = path;
for (;; c++) {
- if (*c == '\0')
+ if (*c == '\0' || *c == ':')
return true;
if (*c != ' ' && *c != '.')
return false;
@@ -1642,13 +1677,13 @@ GIT_INLINE(bool) verify_dotgit_ntfs_generic(const char *name, size_t len, const
if (name[0] == '.' && len >= dotgit_len &&
!strncasecmp(name + 1, dotgit_name, dotgit_len)) {
- return !only_spaces_and_dots(name + dotgit_len + 1);
+ return !ntfs_end_of_filename(name + dotgit_len + 1);
}
/* Detect the basic NTFS shortname with the first six chars */
if (!strncasecmp(name, dotgit_name, 6) && name[6] == '~' &&
name[7] >= '1' && name[7] <= '4')
- return !only_spaces_and_dots(name + 8);
+ return !ntfs_end_of_filename(name + 8);
/* Catch fallback names */
for (i = 0, saw_tilde = 0; i < 8; i++) {
@@ -1670,7 +1705,7 @@ GIT_INLINE(bool) verify_dotgit_ntfs_generic(const char *name, size_t len, const
}
}
- return !only_spaces_and_dots(name + i);
+ return !ntfs_end_of_filename(name + i);
}
GIT_INLINE(bool) verify_char(unsigned char c, unsigned int flags)
@@ -1804,7 +1839,7 @@ GIT_INLINE(unsigned int) dotgit_flags(
git_repository *repo,
unsigned int flags)
{
- int protectHFS = 0, protectNTFS = 0;
+ int protectHFS = 0, protectNTFS = 1;
int error = 0;
flags |= GIT_PATH_REJECT_DOT_GIT_LITERAL;
@@ -1813,16 +1848,12 @@ GIT_INLINE(unsigned int) dotgit_flags(
protectHFS = 1;
#endif
-#ifdef GIT_WIN32
- protectNTFS = 1;
-#endif
-
if (repo && !protectHFS)
error = git_repository__cvar(&protectHFS, repo, GIT_CVAR_PROTECTHFS);
if (!error && protectHFS)
flags |= GIT_PATH_REJECT_DOT_GIT_HFS;
- if (repo && !protectNTFS)
+ if (repo)
error = git_repository__cvar(&protectNTFS, repo, GIT_CVAR_PROTECTNTFS);
if (!error && protectNTFS)
flags |= GIT_PATH_REJECT_DOT_GIT_NTFS;
diff --git a/src/repository.h b/src/repository.h
index fd6400cc1..65b38b5d6 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -110,7 +110,7 @@ typedef enum {
/* core.protectHFS */
GIT_PROTECTHFS_DEFAULT = GIT_CVAR_FALSE,
/* core.protectNTFS */
- GIT_PROTECTNTFS_DEFAULT = GIT_CVAR_FALSE,
+ GIT_PROTECTNTFS_DEFAULT = GIT_CVAR_TRUE,
/* core.fsyncObjectFiles */
GIT_FSYNCOBJECTFILES_DEFAULT = GIT_CVAR_FALSE,
} git_cvar_value;