diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/path.c | 73 | ||||
-rw-r--r-- | src/repository.h | 2 |
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; |