Sync with 2.14.6
* maint-2.14: (28 commits) Git 2.14.6 mingw: handle `subst`-ed "DOS drives" mingw: refuse to access paths with trailing spaces or periods mingw: refuse to access paths with illegal characters unpack-trees: let merged_entry() pass through do_add_entry()'s errors quote-stress-test: offer to test quoting arguments for MSYS2 sh t6130/t9350: prepare for stringent Win32 path validation quote-stress-test: allow skipping some trials quote-stress-test: accept arguments to test via the command-line tests: add a helper to stress test argument quoting mingw: fix quoting of arguments Disallow dubiously-nested submodule git directories protect_ntfs: turn on NTFS protection by default path: also guard `.gitmodules` against NTFS Alternate Data Streams is_ntfs_dotgit(): speed it up mingw: disallow backslash characters in tree objects' file names path: safeguard `.git` against NTFS Alternate Streams Accesses clone --recurse-submodules: prevent name squatting on Windows is_ntfs_dotgit(): only verify the leading segment test-path-utils: offer to run a protectNTFS/protectHFS benchmark ...
This commit is contained in:
98
path.c
98
path.c
@ -1289,37 +1289,77 @@ int daemon_avoid_alias(const char *p)
|
||||
}
|
||||
}
|
||||
|
||||
static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
|
||||
{
|
||||
if (len < skip)
|
||||
return 0;
|
||||
len -= skip;
|
||||
path += skip;
|
||||
while (len-- > 0) {
|
||||
char c = *(path++);
|
||||
if (c != ' ' && c != '.')
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* On NTFS, we need to be careful to disallow certain synonyms of the `.git/`
|
||||
* directory:
|
||||
*
|
||||
* - For historical reasons, file names that end in spaces or periods are
|
||||
* automatically trimmed. Therefore, `.git . . ./` is a valid way to refer
|
||||
* to `.git/`.
|
||||
*
|
||||
* - For other historical reasons, file names that do not conform to the 8.3
|
||||
* format (up to eight characters for the basename, three for the file
|
||||
* extension, certain characters not allowed such as `+`, etc) are associated
|
||||
* with a so-called "short name", at least on the `C:` drive by default.
|
||||
* Which means that `git~1/` is a valid way to refer to `.git/`.
|
||||
*
|
||||
* Note: Technically, `.git/` could receive the short name `git~2` if the
|
||||
* short name `git~1` were already used. In Git, however, we guarantee that
|
||||
* `.git` is the first item in a directory, therefore it will be associated
|
||||
* with the short name `git~1` (unless short names are disabled).
|
||||
*
|
||||
* - For yet other historical reasons, NTFS supports so-called "Alternate Data
|
||||
* Streams", i.e. metadata associated with a given file, referred to via
|
||||
* `<filename>:<stream-name>:<stream-type>`. There exists a default stream
|
||||
* type for directories, allowing `.git/` to be accessed via
|
||||
* `.git::$INDEX_ALLOCATION/`.
|
||||
*
|
||||
* When this function returns 1, it indicates that the specified file/directory
|
||||
* name refers to a `.git` file or directory, or to any of these synonyms, and
|
||||
* Git should therefore not track it.
|
||||
*
|
||||
* For performance reasons, _all_ Alternate Data Streams of `.git/` are
|
||||
* forbidden, not just `::$INDEX_ALLOCATION`.
|
||||
*
|
||||
* This function is intended to be used by `git fsck` even on platforms where
|
||||
* the backslash is a regular filename character, therefore it needs to handle
|
||||
* backlash characters in the provided `name` specially: they are interpreted
|
||||
* as directory separators.
|
||||
*/
|
||||
int is_ntfs_dotgit(const char *name)
|
||||
{
|
||||
size_t len;
|
||||
char c;
|
||||
|
||||
for (len = 0; ; len++)
|
||||
if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) {
|
||||
if (only_spaces_and_periods(name, len, 4) &&
|
||||
!strncasecmp(name, ".git", 4))
|
||||
return 1;
|
||||
if (only_spaces_and_periods(name, len, 5) &&
|
||||
!strncasecmp(name, "git~1", 5))
|
||||
return 1;
|
||||
if (name[len] != '\\')
|
||||
return 0;
|
||||
name += len + 1;
|
||||
len = -1;
|
||||
}
|
||||
/*
|
||||
* Note that when we don't find `.git` or `git~1` we end up with `name`
|
||||
* advanced partway through the string. That's okay, though, as we
|
||||
* return immediately in those cases, without looking at `name` any
|
||||
* further.
|
||||
*/
|
||||
c = *(name++);
|
||||
if (c == '.') {
|
||||
/* .git */
|
||||
if (((c = *(name++)) != 'g' && c != 'G') ||
|
||||
((c = *(name++)) != 'i' && c != 'I') ||
|
||||
((c = *(name++)) != 't' && c != 'T'))
|
||||
return 0;
|
||||
} else if (c == 'g' || c == 'G') {
|
||||
/* git ~1 */
|
||||
if (((c = *(name++)) != 'i' && c != 'I') ||
|
||||
((c = *(name++)) != 't' && c != 'T') ||
|
||||
*(name++) != '~' ||
|
||||
*(name++) != '1')
|
||||
return 0;
|
||||
} else
|
||||
return 0;
|
||||
|
||||
for (;;) {
|
||||
c = *(name++);
|
||||
if (!c || c == '\\' || c == '/' || c == ':')
|
||||
return 1;
|
||||
if (c != '.' && c != ' ')
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int is_ntfs_dot_generic(const char *name,
|
||||
@ -1335,7 +1375,7 @@ static int is_ntfs_dot_generic(const char *name,
|
||||
only_spaces_and_periods:
|
||||
for (;;) {
|
||||
char c = name[i++];
|
||||
if (!c)
|
||||
if (!c || c == ':')
|
||||
return 1;
|
||||
if (c != ' ' && c != '.')
|
||||
return 0;
|
||||
|
Reference in New Issue
Block a user