|
|
1353ca |
From 17f3a3cfacbc2a8c212062f3bbb1b03d668189c0 Mon Sep 17 00:00:00 2001
|
|
|
1353ca |
From: Jeff King <peff@peff.net>
|
|
|
1353ca |
Date: Mon, 30 Apr 2018 03:25:25 -0400
|
|
|
1353ca |
Subject: [PATCH 01/11] submodule-config: verify submodule names as paths
|
|
|
1353ca |
|
|
|
1353ca |
Submodule "names" come from the untrusted .gitmodules file,
|
|
|
1353ca |
but we blindly append them to $GIT_DIR/modules to create our
|
|
|
1353ca |
on-disk repo paths. This means you can do bad things by
|
|
|
1353ca |
putting "../" into the name (among other things).
|
|
|
1353ca |
|
|
|
1353ca |
Let's sanity-check these names to avoid building a path that
|
|
|
1353ca |
can be exploited. There are two main decisions:
|
|
|
1353ca |
|
|
|
1353ca |
1. What should the allowed syntax be?
|
|
|
1353ca |
|
|
|
1353ca |
It's tempting to reuse verify_path(), since submodule
|
|
|
1353ca |
names typically come from in-repo paths. But there are
|
|
|
1353ca |
two reasons not to:
|
|
|
1353ca |
|
|
|
1353ca |
a. It's technically more strict than what we need, as
|
|
|
1353ca |
we really care only about breaking out of the
|
|
|
1353ca |
$GIT_DIR/modules/ hierarchy. E.g., having a
|
|
|
1353ca |
submodule named "foo/.git" isn't actually
|
|
|
1353ca |
dangerous, and it's possible that somebody has
|
|
|
1353ca |
manually given such a funny name.
|
|
|
1353ca |
|
|
|
1353ca |
b. Since we'll eventually use this checking logic in
|
|
|
1353ca |
fsck to prevent downstream repositories, it should
|
|
|
1353ca |
be consistent across platforms. Because
|
|
|
1353ca |
verify_path() relies on is_dir_sep(), it wouldn't
|
|
|
1353ca |
block "foo\..\bar" on a non-Windows machine.
|
|
|
1353ca |
|
|
|
1353ca |
2. Where should we enforce it? These days most of the
|
|
|
1353ca |
.gitmodules reads go through submodule-config.c, so
|
|
|
1353ca |
I've put it there in the reading step. That should
|
|
|
1353ca |
cover all of the C code.
|
|
|
1353ca |
|
|
|
1353ca |
We also construct the name for "git submodule add"
|
|
|
1353ca |
inside the git-submodule.sh script. This is probably
|
|
|
1353ca |
not a big deal for security since the name is coming
|
|
|
1353ca |
from the user anyway, but it would be polite to remind
|
|
|
1353ca |
them if the name they pick is invalid (and we need to
|
|
|
1353ca |
expose the name-checker to the shell anyway for our
|
|
|
1353ca |
test scripts).
|
|
|
1353ca |
|
|
|
1353ca |
This patch issues a warning when reading .gitmodules
|
|
|
1353ca |
and just ignores the related config entry completely.
|
|
|
1353ca |
This will generally end up producing a sensible error,
|
|
|
1353ca |
as it works the same as a .gitmodules file which is
|
|
|
1353ca |
missing a submodule entry (so "submodule update" will
|
|
|
1353ca |
barf, but "git clone --recurse-submodules" will print
|
|
|
1353ca |
an error but not abort the clone.
|
|
|
1353ca |
|
|
|
1353ca |
There is one minor oddity, which is that we print the
|
|
|
1353ca |
warning once per malformed config key (since that's how
|
|
|
1353ca |
the config subsystem gives us the entries). So in the
|
|
|
1353ca |
new test, for example, the user would see three
|
|
|
1353ca |
warnings. That's OK, since the intent is that this case
|
|
|
1353ca |
should never come up outside of malicious repositories
|
|
|
1353ca |
(and then it might even benefit the user to see the
|
|
|
1353ca |
message multiple times).
|
|
|
1353ca |
|
|
|
1353ca |
Credit for finding this vulnerability and the proof of
|
|
|
1353ca |
concept from which the test script was adapted goes to
|
|
|
1353ca |
Etienne Stalmans.
|
|
|
1353ca |
|
|
|
1353ca |
Signed-off-by: Jeff King <peff@peff.net>
|
|
|
1353ca |
---
|
|
|
1353ca |
builtin/submodule--helper.c | 27 +++++++++++++++-
|
|
|
1353ca |
git-submodule.sh | 5 +++
|
|
|
1353ca |
submodule-config.c | 31 ++++++++++++++++++
|
|
|
1353ca |
submodule-config.h | 8 +++++
|
|
|
1353ca |
t/t7415-submodule-names.sh | 76 +++++++++++++++++++++++++++++++++++++++++++++
|
|
|
1353ca |
5 files changed, 146 insertions(+), 1 deletion(-)
|
|
|
1353ca |
create mode 100755 t/t7415-submodule-names.sh
|
|
|
1353ca |
|
|
|
1353ca |
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
|
|
|
1353ca |
index 926d205..5883e6c 100644
|
|
|
1353ca |
--- a/builtin/submodule--helper.c
|
|
|
1353ca |
+++ b/builtin/submodule--helper.c
|
|
|
1353ca |
@@ -842,6 +842,30 @@ static int resolve_relative_path(int argc, const char **argv, const char *prefix
|
|
|
1353ca |
return 0;
|
|
|
1353ca |
}
|
|
|
1353ca |
|
|
|
1353ca |
+/*
|
|
|
1353ca |
+ * Exit non-zero if any of the submodule names given on the command line is
|
|
|
1353ca |
+ * invalid. If no names are given, filter stdin to print only valid names
|
|
|
1353ca |
+ * (which is primarily intended for testing).
|
|
|
1353ca |
+ */
|
|
|
1353ca |
+static int check_name(int argc, const char **argv, const char *prefix)
|
|
|
1353ca |
+{
|
|
|
1353ca |
+ if (argc > 1) {
|
|
|
1353ca |
+ while (*++argv) {
|
|
|
1353ca |
+ if (check_submodule_name(*argv) < 0)
|
|
|
1353ca |
+ return 1;
|
|
|
1353ca |
+ }
|
|
|
1353ca |
+ } else {
|
|
|
1353ca |
+ struct strbuf buf = STRBUF_INIT;
|
|
|
1353ca |
+ while (strbuf_getline(&buf, stdin) != EOF) {
|
|
|
1353ca |
+ if (!check_submodule_name(buf.buf))
|
|
|
1353ca |
+ printf("%s\n", buf.buf);
|
|
|
1353ca |
+ }
|
|
|
1353ca |
+ strbuf_release(&buf;;
|
|
|
1353ca |
+ }
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+}
|
|
|
1353ca |
+
|
|
|
1353ca |
+
|
|
|
1353ca |
struct cmd_struct {
|
|
|
1353ca |
const char *cmd;
|
|
|
1353ca |
int (*fn)(int, const char **, const char *);
|
|
|
1353ca |
@@ -855,7 +879,8 @@ static struct cmd_struct commands[] = {
|
|
|
1353ca |
{"relative-path", resolve_relative_path},
|
|
|
1353ca |
{"resolve-relative-url", resolve_relative_url},
|
|
|
1353ca |
{"resolve-relative-url-test", resolve_relative_url_test},
|
|
|
1353ca |
- {"init", module_init}
|
|
|
1353ca |
+ {"init", module_init},
|
|
|
1353ca |
+ {"check-name", check_name}
|
|
|
1353ca |
};
|
|
|
1353ca |
|
|
|
1353ca |
int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
|
|
|
1353ca |
diff --git a/git-submodule.sh b/git-submodule.sh
|
|
|
1353ca |
index 78fdac9..d82da42 100755
|
|
|
1353ca |
--- a/git-submodule.sh
|
|
|
1353ca |
+++ b/git-submodule.sh
|
|
|
1353ca |
@@ -225,6 +225,11 @@ Use -f if you really want to add it." >&2
|
|
|
1353ca |
sm_name="$sm_path"
|
|
|
1353ca |
fi
|
|
|
1353ca |
|
|
|
1353ca |
+ if ! git submodule--helper check-name "$sm_name"
|
|
|
1353ca |
+ then
|
|
|
1353ca |
+ die "$(eval_gettext "'$sm_name' is not a valid submodule name")"
|
|
|
1353ca |
+ fi
|
|
|
1353ca |
+
|
|
|
1353ca |
# perhaps the path exists and is already a git repo, else clone it
|
|
|
1353ca |
if test -e "$sm_path"
|
|
|
1353ca |
then
|
|
|
1353ca |
diff --git a/submodule-config.c b/submodule-config.c
|
|
|
1353ca |
index 93dd364..dbba74a 100644
|
|
|
1353ca |
--- a/submodule-config.c
|
|
|
1353ca |
+++ b/submodule-config.c
|
|
|
1353ca |
@@ -162,6 +162,31 @@ static struct submodule *cache_lookup_name(struct submodule_cache *cache,
|
|
|
1353ca |
return NULL;
|
|
|
1353ca |
}
|
|
|
1353ca |
|
|
|
1353ca |
+int check_submodule_name(const char *name)
|
|
|
1353ca |
+{
|
|
|
1353ca |
+ /* Disallow empty names */
|
|
|
1353ca |
+ if (!*name)
|
|
|
1353ca |
+ return -1;
|
|
|
1353ca |
+
|
|
|
1353ca |
+ /*
|
|
|
1353ca |
+ * Look for '..' as a path component. Check both '/' and '\\' as
|
|
|
1353ca |
+ * separators rather than is_dir_sep(), because we want the name rules
|
|
|
1353ca |
+ * to be consistent across platforms.
|
|
|
1353ca |
+ */
|
|
|
1353ca |
+ goto in_component; /* always start inside component */
|
|
|
1353ca |
+ while (*name) {
|
|
|
1353ca |
+ char c = *name++;
|
|
|
1353ca |
+ if (c == '/' || c == '\\') {
|
|
|
1353ca |
+in_component:
|
|
|
1353ca |
+ if (name[0] == '.' && name[1] == '.' &&
|
|
|
1353ca |
+ (!name[2] || name[2] == '/' || name[2] == '\\'))
|
|
|
1353ca |
+ return -1;
|
|
|
1353ca |
+ }
|
|
|
1353ca |
+ }
|
|
|
1353ca |
+
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+}
|
|
|
1353ca |
+
|
|
|
1353ca |
static int name_and_item_from_var(const char *var, struct strbuf *name,
|
|
|
1353ca |
struct strbuf *item)
|
|
|
1353ca |
{
|
|
|
1353ca |
@@ -173,6 +198,12 @@ static int name_and_item_from_var(const char *var, struct strbuf *name,
|
|
|
1353ca |
return 0;
|
|
|
1353ca |
|
|
|
1353ca |
strbuf_add(name, subsection, subsection_len);
|
|
|
1353ca |
+ if (check_submodule_name(name->buf) < 0) {
|
|
|
1353ca |
+ warning(_("ignoring suspicious submodule name: %s"), name->buf);
|
|
|
1353ca |
+ strbuf_release(name);
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+ }
|
|
|
1353ca |
+
|
|
|
1353ca |
strbuf_addstr(item, key);
|
|
|
1353ca |
|
|
|
1353ca |
return 1;
|
|
|
1353ca |
diff --git a/submodule-config.h b/submodule-config.h
|
|
|
1353ca |
index e4857f5..6ed7a3c 100644
|
|
|
1353ca |
--- a/submodule-config.h
|
|
|
1353ca |
+++ b/submodule-config.h
|
|
|
1353ca |
@@ -29,4 +29,12 @@ const struct submodule *submodule_from_path(const unsigned char *commit_sha1,
|
|
|
1353ca |
const char *path);
|
|
|
1353ca |
void submodule_free(void);
|
|
|
1353ca |
|
|
|
1353ca |
+/*
|
|
|
1353ca |
+ * Returns 0 if the name is syntactically acceptable as a submodule "name"
|
|
|
1353ca |
+ * (e.g., that may be found in the subsection of a .gitmodules file) and -1
|
|
|
1353ca |
+ * otherwise.
|
|
|
1353ca |
+ */
|
|
|
1353ca |
+int check_submodule_name(const char *name);
|
|
|
1353ca |
+
|
|
|
1353ca |
+
|
|
|
1353ca |
#endif /* SUBMODULE_CONFIG_H */
|
|
|
1353ca |
diff --git a/t/t7415-submodule-names.sh b/t/t7415-submodule-names.sh
|
|
|
1353ca |
new file mode 100755
|
|
|
1353ca |
index 0000000..75fa071
|
|
|
1353ca |
--- /dev/null
|
|
|
1353ca |
+++ b/t/t7415-submodule-names.sh
|
|
|
1353ca |
@@ -0,0 +1,76 @@
|
|
|
1353ca |
+#!/bin/sh
|
|
|
1353ca |
+
|
|
|
1353ca |
+test_description='check handling of .. in submodule names
|
|
|
1353ca |
+
|
|
|
1353ca |
+Exercise the name-checking function on a variety of names, and then give a
|
|
|
1353ca |
+real-world setup that confirms we catch this in practice.
|
|
|
1353ca |
+'
|
|
|
1353ca |
+. ./test-lib.sh
|
|
|
1353ca |
+
|
|
|
1353ca |
+test_expect_success 'check names' '
|
|
|
1353ca |
+ cat >expect <<-\EOF &&
|
|
|
1353ca |
+ valid
|
|
|
1353ca |
+ valid/with/paths
|
|
|
1353ca |
+ EOF
|
|
|
1353ca |
+
|
|
|
1353ca |
+ git submodule--helper check-name >actual <<-\EOF &&
|
|
|
1353ca |
+ valid
|
|
|
1353ca |
+ valid/with/paths
|
|
|
1353ca |
+
|
|
|
1353ca |
+ ../foo
|
|
|
1353ca |
+ /../foo
|
|
|
1353ca |
+ ..\foo
|
|
|
1353ca |
+ \..\foo
|
|
|
1353ca |
+ foo/..
|
|
|
1353ca |
+ foo/../
|
|
|
1353ca |
+ foo\..
|
|
|
1353ca |
+ foo\..\
|
|
|
1353ca |
+ foo/../bar
|
|
|
1353ca |
+ EOF
|
|
|
1353ca |
+
|
|
|
1353ca |
+ test_cmp expect actual
|
|
|
1353ca |
+'
|
|
|
1353ca |
+
|
|
|
1353ca |
+test_expect_success 'create innocent subrepo' '
|
|
|
1353ca |
+ git init innocent &&
|
|
|
1353ca |
+ git -C innocent commit --allow-empty -m foo
|
|
|
1353ca |
+'
|
|
|
1353ca |
+
|
|
|
1353ca |
+test_expect_success 'submodule add refuses invalid names' '
|
|
|
1353ca |
+ test_must_fail \
|
|
|
1353ca |
+ git submodule add --name ../../modules/evil "$PWD/innocent" evil
|
|
|
1353ca |
+'
|
|
|
1353ca |
+
|
|
|
1353ca |
+test_expect_success 'add evil submodule' '
|
|
|
1353ca |
+ git submodule add "$PWD/innocent" evil &&
|
|
|
1353ca |
+
|
|
|
1353ca |
+ mkdir modules &&
|
|
|
1353ca |
+ cp -r .git/modules/evil modules &&
|
|
|
1353ca |
+ write_script modules/evil/hooks/post-checkout <<-\EOF &&
|
|
|
1353ca |
+ echo >&2 "RUNNING POST CHECKOUT"
|
|
|
1353ca |
+ EOF
|
|
|
1353ca |
+
|
|
|
1353ca |
+ git config -f .gitmodules submodule.evil.update checkout &&
|
|
|
1353ca |
+ git config -f .gitmodules --rename-section \
|
|
|
1353ca |
+ submodule.evil submodule.../../modules/evil &&
|
|
|
1353ca |
+ git add modules &&
|
|
|
1353ca |
+ git commit -am evil
|
|
|
1353ca |
+'
|
|
|
1353ca |
+
|
|
|
1353ca |
+# This step seems like it shouldn't be necessary, since the payload is
|
|
|
1353ca |
+# contained entirely in the evil submodule. But due to the vagaries of the
|
|
|
1353ca |
+# submodule code, checking out the evil module will fail unless ".git/modules"
|
|
|
1353ca |
+# exists. Adding another submodule (with a name that sorts before "evil") is an
|
|
|
1353ca |
+# easy way to make sure this is the case in the victim clone.
|
|
|
1353ca |
+test_expect_success 'add other submodule' '
|
|
|
1353ca |
+ git submodule add "$PWD/innocent" another-module &&
|
|
|
1353ca |
+ git add another-module &&
|
|
|
1353ca |
+ git commit -am another
|
|
|
1353ca |
+'
|
|
|
1353ca |
+
|
|
|
1353ca |
+test_expect_success 'clone evil superproject' '
|
|
|
1353ca |
+ git clone --recurse-submodules . victim >output 2>&1 &&
|
|
|
1353ca |
+ ! grep "RUNNING POST CHECKOUT" output
|
|
|
1353ca |
+'
|
|
|
1353ca |
+
|
|
|
1353ca |
+test_done
|
|
|
1353ca |
--
|
|
|
1353ca |
2.14.4
|
|
|
1353ca |
|
|
|
1353ca |
|
|
|
1353ca |
From 42410051db39f2009980ba293ff0f4f1755df06a Mon Sep 17 00:00:00 2001
|
|
|
1353ca |
From: Jeff King <peff@peff.net>
|
|
|
1353ca |
Date: Sun, 13 May 2018 12:09:42 -0400
|
|
|
1353ca |
Subject: [PATCH 02/11] is_ntfs_dotgit: use a size_t for traversing string
|
|
|
1353ca |
|
|
|
1353ca |
We walk through the "name" string using an int, which can
|
|
|
1353ca |
wrap to a negative value and cause us to read random memory
|
|
|
1353ca |
before our array (e.g., by creating a tree with a name >2GB,
|
|
|
1353ca |
since "int" is still 32 bits even on most 64-bit platforms).
|
|
|
1353ca |
Worse, this is easy to trigger during the fsck_tree() check,
|
|
|
1353ca |
which is supposed to be protecting us from malicious
|
|
|
1353ca |
garbage.
|
|
|
1353ca |
|
|
|
1353ca |
Note one bit of trickiness in the existing code: we
|
|
|
1353ca |
sometimes assign -1 to "len" at the end of the loop, and
|
|
|
1353ca |
then rely on the "len++" in the for-loop's increment to take
|
|
|
1353ca |
it back to 0. This is still legal with a size_t, since
|
|
|
1353ca |
assigning -1 will turn into SIZE_MAX, which then wraps
|
|
|
1353ca |
around to 0 on increment.
|
|
|
1353ca |
|
|
|
1353ca |
Signed-off-by: Jeff King <peff@peff.net>
|
|
|
1353ca |
---
|
|
|
1353ca |
path.c | 2 +-
|
|
|
1353ca |
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
1353ca |
|
|
|
1353ca |
diff --git a/path.c b/path.c
|
|
|
1353ca |
index 0778ff0..3f41416 100644
|
|
|
1353ca |
--- a/path.c
|
|
|
1353ca |
+++ b/path.c
|
|
|
1353ca |
@@ -1205,7 +1205,7 @@ static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
|
|
|
1353ca |
|
|
|
1353ca |
int is_ntfs_dotgit(const char *name)
|
|
|
1353ca |
{
|
|
|
1353ca |
- int len;
|
|
|
1353ca |
+ size_t len;
|
|
|
1353ca |
|
|
|
1353ca |
for (len = 0; ; len++)
|
|
|
1353ca |
if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) {
|
|
|
1353ca |
--
|
|
|
1353ca |
2.14.4
|
|
|
1353ca |
|
|
|
1353ca |
|
|
|
1353ca |
From e63ce01d9a387c60754349508352f3bca086bbec Mon Sep 17 00:00:00 2001
|
|
|
1353ca |
From: Jeff King <peff@peff.net>
|
|
|
1353ca |
Date: Wed, 2 May 2018 15:23:45 -0400
|
|
|
1353ca |
Subject: [PATCH 03/11] is_hfs_dotgit: match other .git files
|
|
|
1353ca |
|
|
|
1353ca |
Both verify_path() and fsck match ".git", ".GIT", and other
|
|
|
1353ca |
variants specific to HFS+. Let's allow matching other
|
|
|
1353ca |
special files like ".gitmodules", which we'll later use to
|
|
|
1353ca |
enforce extra restrictions via verify_path() and fsck.
|
|
|
1353ca |
|
|
|
1353ca |
Signed-off-by: Jeff King <peff@peff.net>
|
|
|
1353ca |
---
|
|
|
1353ca |
utf8.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++------------
|
|
|
1353ca |
utf8.h | 5 +++++
|
|
|
1353ca |
2 files changed, 51 insertions(+), 12 deletions(-)
|
|
|
1353ca |
|
|
|
1353ca |
diff --git a/utf8.c b/utf8.c
|
|
|
1353ca |
index 00e10c8..0d406b1 100644
|
|
|
1353ca |
--- a/utf8.c
|
|
|
1353ca |
+++ b/utf8.c
|
|
|
1353ca |
@@ -605,28 +605,33 @@ static ucs_char_t next_hfs_char(const char **in)
|
|
|
1353ca |
}
|
|
|
1353ca |
}
|
|
|
1353ca |
|
|
|
1353ca |
-int is_hfs_dotgit(const char *path)
|
|
|
1353ca |
+static int is_hfs_dot_generic(const char *path,
|
|
|
1353ca |
+ const char *needle, size_t needle_len)
|
|
|
1353ca |
{
|
|
|
1353ca |
ucs_char_t c;
|
|
|
1353ca |
|
|
|
1353ca |
c = next_hfs_char(&path);
|
|
|
1353ca |
if (c != '.')
|
|
|
1353ca |
return 0;
|
|
|
1353ca |
- c = next_hfs_char(&path);
|
|
|
1353ca |
|
|
|
1353ca |
/*
|
|
|
1353ca |
* there's a great deal of other case-folding that occurs
|
|
|
1353ca |
- * in HFS+, but this is enough to catch anything that will
|
|
|
1353ca |
- * convert to ".git"
|
|
|
1353ca |
+ * in HFS+, but this is enough to catch our fairly vanilla
|
|
|
1353ca |
+ * hard-coded needles.
|
|
|
1353ca |
*/
|
|
|
1353ca |
- if (c != 'g' && c != 'G')
|
|
|
1353ca |
- return 0;
|
|
|
1353ca |
- c = next_hfs_char(&path);
|
|
|
1353ca |
- if (c != 'i' && c != 'I')
|
|
|
1353ca |
- return 0;
|
|
|
1353ca |
- c = next_hfs_char(&path);
|
|
|
1353ca |
- if (c != 't' && c != 'T')
|
|
|
1353ca |
- return 0;
|
|
|
1353ca |
+ for (; needle_len > 0; needle++, needle_len--) {
|
|
|
1353ca |
+ c = next_hfs_char(&path);
|
|
|
1353ca |
+
|
|
|
1353ca |
+ /*
|
|
|
1353ca |
+ * We know our needles contain only ASCII, so we clamp here to
|
|
|
1353ca |
+ * make the results of tolower() sane.
|
|
|
1353ca |
+ */
|
|
|
1353ca |
+ if (c > 127)
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+ if (tolower(c) != *needle)
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+ }
|
|
|
1353ca |
+
|
|
|
1353ca |
c = next_hfs_char(&path);
|
|
|
1353ca |
if (c && !is_dir_sep(c))
|
|
|
1353ca |
return 0;
|
|
|
1353ca |
@@ -634,6 +639,35 @@ int is_hfs_dotgit(const char *path)
|
|
|
1353ca |
return 1;
|
|
|
1353ca |
}
|
|
|
1353ca |
|
|
|
1353ca |
+/*
|
|
|
1353ca |
+ * Inline wrapper to make sure the compiler resolves strlen() on literals at
|
|
|
1353ca |
+ * compile time.
|
|
|
1353ca |
+ */
|
|
|
1353ca |
+static inline int is_hfs_dot_str(const char *path, const char *needle)
|
|
|
1353ca |
+{
|
|
|
1353ca |
+ return is_hfs_dot_generic(path, needle, strlen(needle));
|
|
|
1353ca |
+}
|
|
|
1353ca |
+
|
|
|
1353ca |
+int is_hfs_dotgit(const char *path)
|
|
|
1353ca |
+{
|
|
|
1353ca |
+ return is_hfs_dot_str(path, "git");
|
|
|
1353ca |
+}
|
|
|
1353ca |
+
|
|
|
1353ca |
+int is_hfs_dotgitmodules(const char *path)
|
|
|
1353ca |
+{
|
|
|
1353ca |
+ return is_hfs_dot_str(path, "gitmodules");
|
|
|
1353ca |
+}
|
|
|
1353ca |
+
|
|
|
1353ca |
+int is_hfs_dotgitignore(const char *path)
|
|
|
1353ca |
+{
|
|
|
1353ca |
+ return is_hfs_dot_str(path, "gitignore");
|
|
|
1353ca |
+}
|
|
|
1353ca |
+
|
|
|
1353ca |
+int is_hfs_dotgitattributes(const char *path)
|
|
|
1353ca |
+{
|
|
|
1353ca |
+ return is_hfs_dot_str(path, "gitattributes");
|
|
|
1353ca |
+}
|
|
|
1353ca |
+
|
|
|
1353ca |
const char utf8_bom[] = "\357\273\277";
|
|
|
1353ca |
|
|
|
1353ca |
int skip_utf8_bom(char **text, size_t len)
|
|
|
1353ca |
diff --git a/utf8.h b/utf8.h
|
|
|
1353ca |
index 6bbcf31..da19b43 100644
|
|
|
1353ca |
--- a/utf8.h
|
|
|
1353ca |
+++ b/utf8.h
|
|
|
1353ca |
@@ -52,8 +52,13 @@ int mbs_chrlen(const char **text, size_t *remainder_p, const char *encoding);
|
|
|
1353ca |
* The path should be NUL-terminated, but we will match variants of both ".git\0"
|
|
|
1353ca |
* and ".git/..." (but _not_ ".../.git"). This makes it suitable for both fsck
|
|
|
1353ca |
* and verify_path().
|
|
|
1353ca |
+ *
|
|
|
1353ca |
+ * Likewise, the is_hfs_dotgitfoo() variants look for ".gitfoo".
|
|
|
1353ca |
*/
|
|
|
1353ca |
int is_hfs_dotgit(const char *path);
|
|
|
1353ca |
+int is_hfs_dotgitmodules(const char *path);
|
|
|
1353ca |
+int is_hfs_dotgitignore(const char *path);
|
|
|
1353ca |
+int is_hfs_dotgitattributes(const char *path);
|
|
|
1353ca |
|
|
|
1353ca |
typedef enum {
|
|
|
1353ca |
ALIGN_LEFT,
|
|
|
1353ca |
--
|
|
|
1353ca |
2.14.4
|
|
|
1353ca |
|
|
|
1353ca |
|
|
|
1353ca |
From c914f82f17ce42ea03ae1754f263c600e4faa7f3 Mon Sep 17 00:00:00 2001
|
|
|
1353ca |
From: Johannes Schindelin <johannes.schindelin@gmx.de>
|
|
|
1353ca |
Date: Fri, 11 May 2018 16:03:54 +0200
|
|
|
1353ca |
Subject: [PATCH 04/11] is_ntfs_dotgit: match other .git files
|
|
|
1353ca |
|
|
|
1353ca |
When we started to catch NTFS short names that clash with .git, we only
|
|
|
1353ca |
looked for GIT~1. This is sufficient because we only ever clone into an
|
|
|
1353ca |
empty directory, so .git is guaranteed to be the first subdirectory or
|
|
|
1353ca |
file in that directory.
|
|
|
1353ca |
|
|
|
1353ca |
However, even with a fresh clone, .gitmodules is *not* necessarily the
|
|
|
1353ca |
first file to be written that would want the NTFS short name GITMOD~1: a
|
|
|
1353ca |
malicious repository can add .gitmodul0000 and friends, which sorts
|
|
|
1353ca |
before `.gitmodules` and is therefore checked out *first*. For that
|
|
|
1353ca |
reason, we have to test not only for ~1 short names, but for others,
|
|
|
1353ca |
too.
|
|
|
1353ca |
|
|
|
1353ca |
It's hard to just adapt the existing checks in is_ntfs_dotgit(): since
|
|
|
1353ca |
Windows 2000 (i.e., in all Windows versions still supported by Git),
|
|
|
1353ca |
NTFS short names are only generated in the <prefix>~<number> form up to
|
|
|
1353ca |
number 4. After that, a *different* prefix is used, calculated from the
|
|
|
1353ca |
long file name using an undocumented, but stable algorithm.
|
|
|
1353ca |
|
|
|
1353ca |
For example, the short name of .gitmodules would be GITMOD~1, but if it
|
|
|
1353ca |
is taken, and all of ~2, ~3 and ~4 are taken, too, the short name
|
|
|
1353ca |
GI7EBA~1 will be used. From there, collisions are handled by
|
|
|
1353ca |
incrementing the number, shortening the prefix as needed (until ~9999999
|
|
|
1353ca |
is reached, in which case NTFS will not allow the file to be created).
|
|
|
1353ca |
|
|
|
1353ca |
We'd also want to handle .gitignore and .gitattributes, which suffer
|
|
|
1353ca |
from a similar problem, using the fall-back short names GI250A~1 and
|
|
|
1353ca |
GI7D29~1, respectively.
|
|
|
1353ca |
|
|
|
1353ca |
To accommodate for that, we could reimplement the hashing algorithm, but
|
|
|
1353ca |
it is just safer and simpler to provide the known prefixes. This
|
|
|
1353ca |
algorithm has been reverse-engineered and described at
|
|
|
1353ca |
https://usn.pw/blog/gen/2015/06/09/filenames/, which is defunct but
|
|
|
1353ca |
still available via https://web.archive.org/.
|
|
|
1353ca |
|
|
|
1353ca |
These can be recomputed by running the following Perl script:
|
|
|
1353ca |
|
|
|
1353ca |
-- snip --
|
|
|
1353ca |
use warnings;
|
|
|
1353ca |
use strict;
|
|
|
1353ca |
|
|
|
1353ca |
sub compute_short_name_hash ($) {
|
|
|
1353ca |
my $checksum = 0;
|
|
|
1353ca |
foreach (split('', $_[0])) {
|
|
|
1353ca |
$checksum = ($checksum * 0x25 + ord($_)) & 0xffff;
|
|
|
1353ca |
}
|
|
|
1353ca |
|
|
|
1353ca |
$checksum = ($checksum * 314159269) & 0xffffffff;
|
|
|
1353ca |
$checksum = 1 + (~$checksum & 0x7fffffff) if ($checksum & 0x80000000);
|
|
|
1353ca |
$checksum -= (($checksum * 1152921497) >> 60) * 1000000007;
|
|
|
1353ca |
|
|
|
1353ca |
return scalar reverse sprintf("%x", $checksum & 0xffff);
|
|
|
1353ca |
}
|
|
|
1353ca |
|
|
|
1353ca |
print compute_short_name_hash($ARGV[0]);
|
|
|
1353ca |
-- snap --
|
|
|
1353ca |
|
|
|
1353ca |
E.g., running that with the argument ".gitignore" will
|
|
|
1353ca |
result in "250a" (which then becomes "gi250a" in the code).
|
|
|
1353ca |
|
|
|
1353ca |
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
|
|
|
1353ca |
Signed-off-by: Jeff King <peff@peff.net>
|
|
|
1353ca |
---
|
|
|
1353ca |
cache.h | 10 +++++++-
|
|
|
1353ca |
path.c | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
1353ca |
2 files changed, 93 insertions(+), 1 deletion(-)
|
|
|
1353ca |
|
|
|
1353ca |
diff --git a/cache.h b/cache.h
|
|
|
1353ca |
index 17afd25..fbbe933 100644
|
|
|
1353ca |
--- a/cache.h
|
|
|
1353ca |
+++ b/cache.h
|
|
|
1353ca |
@@ -1032,7 +1032,15 @@ int normalize_path_copy(char *dst, const char *src);
|
|
|
1353ca |
int longest_ancestor_length(const char *path, struct string_list *prefixes);
|
|
|
1353ca |
char *strip_path_suffix(const char *path, const char *suffix);
|
|
|
1353ca |
int daemon_avoid_alias(const char *path);
|
|
|
1353ca |
-extern int is_ntfs_dotgit(const char *name);
|
|
|
1353ca |
+
|
|
|
1353ca |
+/*
|
|
|
1353ca |
+ * These functions match their is_hfs_dotgit() counterparts; see utf8.h for
|
|
|
1353ca |
+ * details.
|
|
|
1353ca |
+ */
|
|
|
1353ca |
+int is_ntfs_dotgit(const char *name);
|
|
|
1353ca |
+int is_ntfs_dotgitmodules(const char *name);
|
|
|
1353ca |
+int is_ntfs_dotgitignore(const char *name);
|
|
|
1353ca |
+int is_ntfs_dotgitattributes(const char *name);
|
|
|
1353ca |
|
|
|
1353ca |
/*
|
|
|
1353ca |
* Returns true iff "str" could be confused as a command-line option when
|
|
|
1353ca |
diff --git a/path.c b/path.c
|
|
|
1353ca |
index 3f41416..34d95a4 100644
|
|
|
1353ca |
--- a/path.c
|
|
|
1353ca |
+++ b/path.c
|
|
|
1353ca |
@@ -1222,6 +1222,90 @@ int is_ntfs_dotgit(const char *name)
|
|
|
1353ca |
}
|
|
|
1353ca |
}
|
|
|
1353ca |
|
|
|
1353ca |
+static int is_ntfs_dot_generic(const char *name,
|
|
|
1353ca |
+ const char *dotgit_name,
|
|
|
1353ca |
+ size_t len,
|
|
|
1353ca |
+ const char *dotgit_ntfs_shortname_prefix)
|
|
|
1353ca |
+{
|
|
|
1353ca |
+ int saw_tilde;
|
|
|
1353ca |
+ size_t i;
|
|
|
1353ca |
+
|
|
|
1353ca |
+ if ((name[0] == '.' && !strncasecmp(name + 1, dotgit_name, len))) {
|
|
|
1353ca |
+ i = len + 1;
|
|
|
1353ca |
+only_spaces_and_periods:
|
|
|
1353ca |
+ for (;;) {
|
|
|
1353ca |
+ char c = name[i++];
|
|
|
1353ca |
+ if (!c)
|
|
|
1353ca |
+ return 1;
|
|
|
1353ca |
+ if (c != ' ' && c != '.')
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+ }
|
|
|
1353ca |
+ }
|
|
|
1353ca |
+
|
|
|
1353ca |
+ /*
|
|
|
1353ca |
+ * Is it a regular NTFS short name, i.e. shortened to 6 characters,
|
|
|
1353ca |
+ * followed by ~1, ... ~4?
|
|
|
1353ca |
+ */
|
|
|
1353ca |
+ if (!strncasecmp(name, dotgit_name, 6) && name[6] == '~' &&
|
|
|
1353ca |
+ name[7] >= '1' && name[7] <= '4') {
|
|
|
1353ca |
+ i = 8;
|
|
|
1353ca |
+ goto only_spaces_and_periods;
|
|
|
1353ca |
+ }
|
|
|
1353ca |
+
|
|
|
1353ca |
+ /*
|
|
|
1353ca |
+ * Is it a fall-back NTFS short name (for details, see
|
|
|
1353ca |
+ * https://en.wikipedia.org/wiki/8.3_filename?
|
|
|
1353ca |
+ */
|
|
|
1353ca |
+ for (i = 0, saw_tilde = 0; i < 8; i++)
|
|
|
1353ca |
+ if (name[i] == '\0')
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+ else if (saw_tilde) {
|
|
|
1353ca |
+ if (name[i] < '0' || name[i] > '9')
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+ } else if (name[i] == '~') {
|
|
|
1353ca |
+ if (name[++i] < '1' || name[i] > '9')
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+ saw_tilde = 1;
|
|
|
1353ca |
+ } else if (i >= 6)
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+ else if (name[i] < 0) {
|
|
|
1353ca |
+ /*
|
|
|
1353ca |
+ * We know our needles contain only ASCII, so we clamp
|
|
|
1353ca |
+ * here to make the results of tolower() sane.
|
|
|
1353ca |
+ */
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+ } else if (tolower(name[i]) != dotgit_ntfs_shortname_prefix[i])
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+
|
|
|
1353ca |
+ goto only_spaces_and_periods;
|
|
|
1353ca |
+}
|
|
|
1353ca |
+
|
|
|
1353ca |
+/*
|
|
|
1353ca |
+ * Inline helper to make sure compiler resolves strlen() on literals at
|
|
|
1353ca |
+ * compile time.
|
|
|
1353ca |
+ */
|
|
|
1353ca |
+static inline int is_ntfs_dot_str(const char *name, const char *dotgit_name,
|
|
|
1353ca |
+ const char *dotgit_ntfs_shortname_prefix)
|
|
|
1353ca |
+{
|
|
|
1353ca |
+ return is_ntfs_dot_generic(name, dotgit_name, strlen(dotgit_name),
|
|
|
1353ca |
+ dotgit_ntfs_shortname_prefix);
|
|
|
1353ca |
+}
|
|
|
1353ca |
+
|
|
|
1353ca |
+int is_ntfs_dotgitmodules(const char *name)
|
|
|
1353ca |
+{
|
|
|
1353ca |
+ return is_ntfs_dot_str(name, "gitmodules", "gi7eba");
|
|
|
1353ca |
+}
|
|
|
1353ca |
+
|
|
|
1353ca |
+int is_ntfs_dotgitignore(const char *name)
|
|
|
1353ca |
+{
|
|
|
1353ca |
+ return is_ntfs_dot_str(name, "gitignore", "gi250a");
|
|
|
1353ca |
+}
|
|
|
1353ca |
+
|
|
|
1353ca |
+int is_ntfs_dotgitattributes(const char *name)
|
|
|
1353ca |
+{
|
|
|
1353ca |
+ return is_ntfs_dot_str(name, "gitattributes", "gi7d29");
|
|
|
1353ca |
+}
|
|
|
1353ca |
+
|
|
|
1353ca |
int looks_like_command_line_option(const char *str)
|
|
|
1353ca |
{
|
|
|
1353ca |
return str && str[0] == '-';
|
|
|
1353ca |
--
|
|
|
1353ca |
2.14.4
|
|
|
1353ca |
|
|
|
1353ca |
|
|
|
1353ca |
From 4c8e72db9864e150f163d1b9da7b0e4e15f49585 Mon Sep 17 00:00:00 2001
|
|
|
1353ca |
From: Johannes Schindelin <johannes.schindelin@gmx.de>
|
|
|
1353ca |
Date: Sat, 12 May 2018 22:16:51 +0200
|
|
|
1353ca |
Subject: [PATCH 05/11] is_{hfs,ntfs}_dotgitmodules: add tests
|
|
|
1353ca |
|
|
|
1353ca |
This tests primarily for NTFS issues, but also adds one example of an
|
|
|
1353ca |
HFS+ issue.
|
|
|
1353ca |
|
|
|
1353ca |
Thanks go to Congyi Wu for coming up with the list of examples where
|
|
|
1353ca |
NTFS would possibly equate the filename with `.gitmodules`.
|
|
|
1353ca |
|
|
|
1353ca |
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
|
|
|
1353ca |
Signed-off-by: Jeff King <peff@peff.net>
|
|
|
1353ca |
---
|
|
|
1353ca |
t/helper/test-path-utils.c | 20 +++++++++++
|
|
|
1353ca |
t/t0060-path-utils.sh | 86 ++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
1353ca |
2 files changed, 106 insertions(+)
|
|
|
1353ca |
|
|
|
1353ca |
diff --git a/t/helper/test-path-utils.c b/t/helper/test-path-utils.c
|
|
|
1353ca |
index ba805b3..e75b146 100644
|
|
|
1353ca |
--- a/t/helper/test-path-utils.c
|
|
|
1353ca |
+++ b/t/helper/test-path-utils.c
|
|
|
1353ca |
@@ -1,5 +1,6 @@
|
|
|
1353ca |
#include "cache.h"
|
|
|
1353ca |
#include "string-list.h"
|
|
|
1353ca |
+#include "utf8.h"
|
|
|
1353ca |
|
|
|
1353ca |
/*
|
|
|
1353ca |
* A "string_list_each_func_t" function that normalizes an entry from
|
|
|
1353ca |
@@ -156,6 +157,11 @@ static struct test_data dirname_data[] = {
|
|
|
1353ca |
{ NULL, NULL }
|
|
|
1353ca |
};
|
|
|
1353ca |
|
|
|
1353ca |
+static int is_dotgitmodules(const char *path)
|
|
|
1353ca |
+{
|
|
|
1353ca |
+ return is_hfs_dotgitmodules(path) || is_ntfs_dotgitmodules(path);
|
|
|
1353ca |
+}
|
|
|
1353ca |
+
|
|
|
1353ca |
int main(int argc, char **argv)
|
|
|
1353ca |
{
|
|
|
1353ca |
if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
|
|
|
1353ca |
@@ -256,6 +262,20 @@ int main(int argc, char **argv)
|
|
|
1353ca |
if (argc == 2 && !strcmp(argv[1], "dirname"))
|
|
|
1353ca |
return test_function(dirname_data, dirname, argv[1]);
|
|
|
1353ca |
|
|
|
1353ca |
+ if (argc > 2 && !strcmp(argv[1], "is_dotgitmodules")) {
|
|
|
1353ca |
+ int res = 0, expect = 1, i;
|
|
|
1353ca |
+ for (i = 2; i < argc; i++)
|
|
|
1353ca |
+ if (!strcmp("--not", argv[i]))
|
|
|
1353ca |
+ expect = !expect;
|
|
|
1353ca |
+ else if (expect != is_dotgitmodules(argv[i]))
|
|
|
1353ca |
+ res = error("'%s' is %s.gitmodules", argv[i],
|
|
|
1353ca |
+ expect ? "not " : "");
|
|
|
1353ca |
+ else
|
|
|
1353ca |
+ fprintf(stderr, "ok: '%s' is %s.gitmodules\n",
|
|
|
1353ca |
+ argv[i], expect ? "" : "not ");
|
|
|
1353ca |
+ return !!res;
|
|
|
1353ca |
+ }
|
|
|
1353ca |
+
|
|
|
1353ca |
fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
|
|
|
1353ca |
argv[1] ? argv[1] : "(there was none)");
|
|
|
1353ca |
return 1;
|
|
|
1353ca |
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
|
|
|
1353ca |
index bf2deee..a1c9bbd 100755
|
|
|
1353ca |
--- a/t/t0060-path-utils.sh
|
|
|
1353ca |
+++ b/t/t0060-path-utils.sh
|
|
|
1353ca |
@@ -344,4 +344,90 @@ test_submodule_relative_url "(null)" "ssh://hostname:22/repo" "../subrepo" "ssh:
|
|
|
1353ca |
test_submodule_relative_url "(null)" "user@host:path/to/repo" "../subrepo" "user@host:path/to/subrepo"
|
|
|
1353ca |
test_submodule_relative_url "(null)" "user@host:repo" "../subrepo" "user@host:subrepo"
|
|
|
1353ca |
|
|
|
1353ca |
+test_expect_success 'match .gitmodules' '
|
|
|
1353ca |
+ test-path-utils is_dotgitmodules \
|
|
|
1353ca |
+ .gitmodules \
|
|
|
1353ca |
+ \
|
|
|
1353ca |
+ .git${u200c}modules \
|
|
|
1353ca |
+ \
|
|
|
1353ca |
+ .Gitmodules \
|
|
|
1353ca |
+ .gitmoduleS \
|
|
|
1353ca |
+ \
|
|
|
1353ca |
+ ".gitmodules " \
|
|
|
1353ca |
+ ".gitmodules." \
|
|
|
1353ca |
+ ".gitmodules " \
|
|
|
1353ca |
+ ".gitmodules. " \
|
|
|
1353ca |
+ ".gitmodules ." \
|
|
|
1353ca |
+ ".gitmodules.." \
|
|
|
1353ca |
+ ".gitmodules " \
|
|
|
1353ca |
+ ".gitmodules. " \
|
|
|
1353ca |
+ ".gitmodules . " \
|
|
|
1353ca |
+ ".gitmodules ." \
|
|
|
1353ca |
+ \
|
|
|
1353ca |
+ ".Gitmodules " \
|
|
|
1353ca |
+ ".Gitmodules." \
|
|
|
1353ca |
+ ".Gitmodules " \
|
|
|
1353ca |
+ ".Gitmodules. " \
|
|
|
1353ca |
+ ".Gitmodules ." \
|
|
|
1353ca |
+ ".Gitmodules.." \
|
|
|
1353ca |
+ ".Gitmodules " \
|
|
|
1353ca |
+ ".Gitmodules. " \
|
|
|
1353ca |
+ ".Gitmodules . " \
|
|
|
1353ca |
+ ".Gitmodules ." \
|
|
|
1353ca |
+ \
|
|
|
1353ca |
+ GITMOD~1 \
|
|
|
1353ca |
+ gitmod~1 \
|
|
|
1353ca |
+ GITMOD~2 \
|
|
|
1353ca |
+ gitmod~3 \
|
|
|
1353ca |
+ GITMOD~4 \
|
|
|
1353ca |
+ \
|
|
|
1353ca |
+ "GITMOD~1 " \
|
|
|
1353ca |
+ "gitmod~2." \
|
|
|
1353ca |
+ "GITMOD~3 " \
|
|
|
1353ca |
+ "gitmod~4. " \
|
|
|
1353ca |
+ "GITMOD~1 ." \
|
|
|
1353ca |
+ "gitmod~2 " \
|
|
|
1353ca |
+ "GITMOD~3. " \
|
|
|
1353ca |
+ "gitmod~4 . " \
|
|
|
1353ca |
+ \
|
|
|
1353ca |
+ GI7EBA~1 \
|
|
|
1353ca |
+ gi7eba~9 \
|
|
|
1353ca |
+ \
|
|
|
1353ca |
+ GI7EB~10 \
|
|
|
1353ca |
+ GI7EB~11 \
|
|
|
1353ca |
+ GI7EB~99 \
|
|
|
1353ca |
+ GI7EB~10 \
|
|
|
1353ca |
+ GI7E~100 \
|
|
|
1353ca |
+ GI7E~101 \
|
|
|
1353ca |
+ GI7E~999 \
|
|
|
1353ca |
+ ~1000000 \
|
|
|
1353ca |
+ ~9999999 \
|
|
|
1353ca |
+ \
|
|
|
1353ca |
+ --not \
|
|
|
1353ca |
+ ".gitmodules x" \
|
|
|
1353ca |
+ ".gitmodules .x" \
|
|
|
1353ca |
+ \
|
|
|
1353ca |
+ " .gitmodules" \
|
|
|
1353ca |
+ \
|
|
|
1353ca |
+ ..gitmodules \
|
|
|
1353ca |
+ \
|
|
|
1353ca |
+ gitmodules \
|
|
|
1353ca |
+ \
|
|
|
1353ca |
+ .gitmodule \
|
|
|
1353ca |
+ \
|
|
|
1353ca |
+ ".gitmodules x " \
|
|
|
1353ca |
+ ".gitmodules .x" \
|
|
|
1353ca |
+ \
|
|
|
1353ca |
+ GI7EBA~ \
|
|
|
1353ca |
+ GI7EBA~0 \
|
|
|
1353ca |
+ GI7EBA~~1 \
|
|
|
1353ca |
+ GI7EBA~X \
|
|
|
1353ca |
+ Gx7EBA~1 \
|
|
|
1353ca |
+ GI7EBX~1 \
|
|
|
1353ca |
+ \
|
|
|
1353ca |
+ GI7EB~1 \
|
|
|
1353ca |
+ GI7EB~01 \
|
|
|
1353ca |
+ GI7EB~1X
|
|
|
1353ca |
+'
|
|
|
1353ca |
+
|
|
|
1353ca |
test_done
|
|
|
1353ca |
--
|
|
|
1353ca |
2.14.4
|
|
|
1353ca |
|
|
|
1353ca |
|
|
|
1353ca |
From f57dd60d921e31b78e42ad9f91c333ab19138deb Mon Sep 17 00:00:00 2001
|
|
|
1353ca |
From: Jeff King <peff@peff.net>
|
|
|
1353ca |
Date: Sun, 13 May 2018 12:57:14 -0400
|
|
|
1353ca |
Subject: [PATCH 06/11] skip_prefix: add case-insensitive variant
|
|
|
1353ca |
|
|
|
1353ca |
We have the convenient skip_prefix() helper, but if you want
|
|
|
1353ca |
to do case-insensitive matching, you're stuck doing it by
|
|
|
1353ca |
hand. We could add an extra parameter to the function to
|
|
|
1353ca |
let callers ask for this, but the function is small and
|
|
|
1353ca |
somewhat performance-critical. Let's just re-implement it
|
|
|
1353ca |
for the case-insensitive version.
|
|
|
1353ca |
|
|
|
1353ca |
Signed-off-by: Jeff King <peff@peff.net>
|
|
|
1353ca |
---
|
|
|
1353ca |
git-compat-util.h | 17 +++++++++++++++++
|
|
|
1353ca |
1 file changed, 17 insertions(+)
|
|
|
1353ca |
|
|
|
1353ca |
diff --git a/git-compat-util.h b/git-compat-util.h
|
|
|
1353ca |
index 49d4029..7ad6e59 100644
|
|
|
1353ca |
--- a/git-compat-util.h
|
|
|
1353ca |
+++ b/git-compat-util.h
|
|
|
1353ca |
@@ -909,6 +909,23 @@ static inline int sane_iscase(int x, int is_lower)
|
|
|
1353ca |
return (x & 0x20) == 0;
|
|
|
1353ca |
}
|
|
|
1353ca |
|
|
|
1353ca |
+/*
|
|
|
1353ca |
+ * Like skip_prefix, but compare case-insensitively. Note that the comparison
|
|
|
1353ca |
+ * is done via tolower(), so it is strictly ASCII (no multi-byte characters or
|
|
|
1353ca |
+ * locale-specific conversions).
|
|
|
1353ca |
+ */
|
|
|
1353ca |
+static inline int skip_iprefix(const char *str, const char *prefix,
|
|
|
1353ca |
+ const char **out)
|
|
|
1353ca |
+{
|
|
|
1353ca |
+ do {
|
|
|
1353ca |
+ if (!*prefix) {
|
|
|
1353ca |
+ *out = str;
|
|
|
1353ca |
+ return 1;
|
|
|
1353ca |
+ }
|
|
|
1353ca |
+ } while (tolower(*str++) == tolower(*prefix++));
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+}
|
|
|
1353ca |
+
|
|
|
1353ca |
static inline int strtoul_ui(char const *s, int base, unsigned int *result)
|
|
|
1353ca |
{
|
|
|
1353ca |
unsigned long ul;
|
|
|
1353ca |
--
|
|
|
1353ca |
2.14.4
|
|
|
1353ca |
|
|
|
1353ca |
|
|
|
1353ca |
From da0dec01dc5fb1d43732ad997d30bed853025a6c Mon Sep 17 00:00:00 2001
|
|
|
1353ca |
From: Jeff King <peff@peff.net>
|
|
|
1353ca |
Date: Sun, 13 May 2018 13:00:23 -0400
|
|
|
1353ca |
Subject: [PATCH 07/11] verify_path: drop clever fallthrough
|
|
|
1353ca |
|
|
|
1353ca |
We check ".git" and ".." in the same switch statement, and
|
|
|
1353ca |
fall through the cases to share the end-of-component check.
|
|
|
1353ca |
While this saves us a line or two, it makes modifying the
|
|
|
1353ca |
function much harder. Let's just write it out.
|
|
|
1353ca |
|
|
|
1353ca |
Signed-off-by: Jeff King <peff@peff.net>
|
|
|
1353ca |
---
|
|
|
1353ca |
read-cache.c | 8 ++++----
|
|
|
1353ca |
1 file changed, 4 insertions(+), 4 deletions(-)
|
|
|
1353ca |
|
|
|
1353ca |
diff --git a/read-cache.c b/read-cache.c
|
|
|
1353ca |
index db27766..b8a2a06 100644
|
|
|
1353ca |
--- a/read-cache.c
|
|
|
1353ca |
+++ b/read-cache.c
|
|
|
1353ca |
@@ -787,8 +787,7 @@ static int verify_dotfile(const char *rest)
|
|
|
1353ca |
|
|
|
1353ca |
switch (*rest) {
|
|
|
1353ca |
/*
|
|
|
1353ca |
- * ".git" followed by NUL or slash is bad. This
|
|
|
1353ca |
- * shares the path end test with the ".." case.
|
|
|
1353ca |
+ * ".git" followed by NUL or slash is bad.
|
|
|
1353ca |
*/
|
|
|
1353ca |
case 'g':
|
|
|
1353ca |
case 'G':
|
|
|
1353ca |
@@ -796,8 +795,9 @@ static int verify_dotfile(const char *rest)
|
|
|
1353ca |
break;
|
|
|
1353ca |
if (rest[2] != 't' && rest[2] != 'T')
|
|
|
1353ca |
break;
|
|
|
1353ca |
- rest += 2;
|
|
|
1353ca |
- /* fallthrough */
|
|
|
1353ca |
+ if (rest[3] == '\0' || is_dir_sep(rest[3]))
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+ break;
|
|
|
1353ca |
case '.':
|
|
|
1353ca |
if (rest[1] == '\0' || is_dir_sep(rest[1]))
|
|
|
1353ca |
return 0;
|
|
|
1353ca |
--
|
|
|
1353ca |
2.14.4
|
|
|
1353ca |
|
|
|
1353ca |
|
|
|
1353ca |
From 392a029f659dc735a445ca16349b98c0c63bcee6 Mon Sep 17 00:00:00 2001
|
|
|
1353ca |
From: Jeff King <peff@peff.net>
|
|
|
1353ca |
Date: Tue, 15 May 2018 09:56:50 -0400
|
|
|
1353ca |
Subject: [PATCH 08/11] verify_dotfile: mention case-insensitivity in comment
|
|
|
1353ca |
|
|
|
1353ca |
We're more restrictive than we need to be in matching ".GIT"
|
|
|
1353ca |
on case-sensitive filesystems; let's make a note that this
|
|
|
1353ca |
is intentional.
|
|
|
1353ca |
|
|
|
1353ca |
Signed-off-by: Jeff King <peff@peff.net>
|
|
|
1353ca |
---
|
|
|
1353ca |
read-cache.c | 5 ++++-
|
|
|
1353ca |
1 file changed, 4 insertions(+), 1 deletion(-)
|
|
|
1353ca |
|
|
|
1353ca |
diff --git a/read-cache.c b/read-cache.c
|
|
|
1353ca |
index b8a2a06..88ed49c 100644
|
|
|
1353ca |
--- a/read-cache.c
|
|
|
1353ca |
+++ b/read-cache.c
|
|
|
1353ca |
@@ -787,7 +787,10 @@ static int verify_dotfile(const char *rest)
|
|
|
1353ca |
|
|
|
1353ca |
switch (*rest) {
|
|
|
1353ca |
/*
|
|
|
1353ca |
- * ".git" followed by NUL or slash is bad.
|
|
|
1353ca |
+ * ".git" followed by NUL or slash is bad. Note that we match
|
|
|
1353ca |
+ * case-insensitively here, even if ignore_case is not set.
|
|
|
1353ca |
+ * This outlaws ".GIT" everywhere out of an abundance of caution,
|
|
|
1353ca |
+ * since there's really no good reason to allow it.
|
|
|
1353ca |
*/
|
|
|
1353ca |
case 'g':
|
|
|
1353ca |
case 'G':
|
|
|
1353ca |
--
|
|
|
1353ca |
2.14.4
|
|
|
1353ca |
|
|
|
1353ca |
|
|
|
1353ca |
From 95a1ce8bd49ad78d9266af969fa6a9b5476ad118 Mon Sep 17 00:00:00 2001
|
|
|
1353ca |
From: Jeff King <peff@peff.net>
|
|
|
1353ca |
Date: Mon, 14 May 2018 11:00:56 -0400
|
|
|
1353ca |
Subject: [PATCH 09/11] update-index: stat updated files earlier
|
|
|
1353ca |
|
|
|
1353ca |
In the update_one(), we check verify_path() on the proposed
|
|
|
1353ca |
path before doing anything else. In preparation for having
|
|
|
1353ca |
verify_path() look at the file mode, let's stat the file
|
|
|
1353ca |
earlier, so we can check the mode accurately.
|
|
|
1353ca |
|
|
|
1353ca |
This is made a bit trickier by the fact that this function
|
|
|
1353ca |
only does an lstat in a few code paths (the ones that flow
|
|
|
1353ca |
down through process_path()). So we can speculatively do the
|
|
|
1353ca |
lstat() here and pass the results down, and just use a dummy
|
|
|
1353ca |
mode for cases where we won't actually be updating the index
|
|
|
1353ca |
from the filesystem.
|
|
|
1353ca |
|
|
|
1353ca |
Signed-off-by: Jeff King <peff@peff.net>
|
|
|
1353ca |
---
|
|
|
1353ca |
builtin/update-index.c | 25 +++++++++++++++++--------
|
|
|
1353ca |
1 file changed, 17 insertions(+), 8 deletions(-)
|
|
|
1353ca |
|
|
|
1353ca |
diff --git a/builtin/update-index.c b/builtin/update-index.c
|
|
|
1353ca |
index b8b8522..0c5be84 100644
|
|
|
1353ca |
--- a/builtin/update-index.c
|
|
|
1353ca |
+++ b/builtin/update-index.c
|
|
|
1353ca |
@@ -354,10 +354,9 @@ static int process_directory(const char *path, int len, struct stat *st)
|
|
|
1353ca |
return error("%s: is a directory - add files inside instead", path);
|
|
|
1353ca |
}
|
|
|
1353ca |
|
|
|
1353ca |
-static int process_path(const char *path)
|
|
|
1353ca |
+static int process_path(const char *path, struct stat *st, int stat_errno)
|
|
|
1353ca |
{
|
|
|
1353ca |
int pos, len;
|
|
|
1353ca |
- struct stat st;
|
|
|
1353ca |
const struct cache_entry *ce;
|
|
|
1353ca |
|
|
|
1353ca |
len = strlen(path);
|
|
|
1353ca |
@@ -381,13 +380,13 @@ static int process_path(const char *path)
|
|
|
1353ca |
* First things first: get the stat information, to decide
|
|
|
1353ca |
* what to do about the pathname!
|
|
|
1353ca |
*/
|
|
|
1353ca |
- if (lstat(path, &st) < 0)
|
|
|
1353ca |
- return process_lstat_error(path, errno);
|
|
|
1353ca |
+ if (stat_errno)
|
|
|
1353ca |
+ return process_lstat_error(path, stat_errno);
|
|
|
1353ca |
|
|
|
1353ca |
- if (S_ISDIR(st.st_mode))
|
|
|
1353ca |
- return process_directory(path, len, &st);
|
|
|
1353ca |
+ if (S_ISDIR(st->st_mode))
|
|
|
1353ca |
+ return process_directory(path, len, st);
|
|
|
1353ca |
|
|
|
1353ca |
- return add_one_path(ce, path, len, &st);
|
|
|
1353ca |
+ return add_one_path(ce, path, len, st);
|
|
|
1353ca |
}
|
|
|
1353ca |
|
|
|
1353ca |
static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
|
|
|
1353ca |
@@ -451,6 +450,16 @@ static void chmod_path(int flip, const char *path)
|
|
|
1353ca |
|
|
|
1353ca |
static void update_one(const char *path)
|
|
|
1353ca |
{
|
|
|
1353ca |
+ int stat_errno = 0;
|
|
|
1353ca |
+ struct stat st;
|
|
|
1353ca |
+
|
|
|
1353ca |
+ if (mark_valid_only || mark_skip_worktree_only || force_remove)
|
|
|
1353ca |
+ st.st_mode = 0;
|
|
|
1353ca |
+ else if (lstat(path, &st) < 0) {
|
|
|
1353ca |
+ st.st_mode = 0;
|
|
|
1353ca |
+ stat_errno = errno;
|
|
|
1353ca |
+ } /* else stat is valid */
|
|
|
1353ca |
+
|
|
|
1353ca |
if (!verify_path(path)) {
|
|
|
1353ca |
fprintf(stderr, "Ignoring path %s\n", path);
|
|
|
1353ca |
return;
|
|
|
1353ca |
@@ -472,7 +481,7 @@ static void update_one(const char *path)
|
|
|
1353ca |
report("remove '%s'", path);
|
|
|
1353ca |
return;
|
|
|
1353ca |
}
|
|
|
1353ca |
- if (process_path(path))
|
|
|
1353ca |
+ if (process_path(path, &st, stat_errno))
|
|
|
1353ca |
die("Unable to process path %s", path);
|
|
|
1353ca |
report("add '%s'", path);
|
|
|
1353ca |
}
|
|
|
1353ca |
--
|
|
|
1353ca |
2.14.4
|
|
|
1353ca |
|
|
|
1353ca |
|
|
|
1353ca |
From 1d5af177a0853d286069176b11f661ed0e069faa Mon Sep 17 00:00:00 2001
|
|
|
1353ca |
From: Jeff King <peff@peff.net>
|
|
|
1353ca |
Date: Fri, 4 May 2018 20:03:35 -0400
|
|
|
1353ca |
Subject: [PATCH 10/11] verify_path: disallow symlinks in .gitmodules
|
|
|
1353ca |
|
|
|
1353ca |
There are a few reasons it's not a good idea to make
|
|
|
1353ca |
.gitmodules a symlink, including:
|
|
|
1353ca |
|
|
|
1353ca |
1. It won't be portable to systems without symlinks.
|
|
|
1353ca |
|
|
|
1353ca |
2. It may behave inconsistently, since Git may look at
|
|
|
1353ca |
this file in the index or a tree without bothering to
|
|
|
1353ca |
resolve any symbolic links. We don't do this _yet_, but
|
|
|
1353ca |
the config infrastructure is there and it's planned for
|
|
|
1353ca |
the future.
|
|
|
1353ca |
|
|
|
1353ca |
With some clever code, we could make (2) work. And some
|
|
|
1353ca |
people may not care about (1) if they only work on one
|
|
|
1353ca |
platform. But there are a few security reasons to simply
|
|
|
1353ca |
disallow it:
|
|
|
1353ca |
|
|
|
1353ca |
a. A symlinked .gitmodules file may circumvent any fsck
|
|
|
1353ca |
checks of the content.
|
|
|
1353ca |
|
|
|
1353ca |
b. Git may read and write from the on-disk file without
|
|
|
1353ca |
sanity checking the symlink target. So for example, if
|
|
|
1353ca |
you link ".gitmodules" to "../oops" and run "git
|
|
|
1353ca |
submodule add", we'll write to the file "oops" outside
|
|
|
1353ca |
the repository.
|
|
|
1353ca |
|
|
|
1353ca |
Again, both of those are problems that _could_ be solved
|
|
|
1353ca |
with sufficient code, but given the complications in (1) and
|
|
|
1353ca |
(2), we're better off just outlawing it explicitly.
|
|
|
1353ca |
|
|
|
1353ca |
Note the slightly tricky call to verify_path() in
|
|
|
1353ca |
update-index's update_one(). There we may not have a mode if
|
|
|
1353ca |
we're not updating from the filesystem (e.g., we might just
|
|
|
1353ca |
be removing the file). Passing "0" as the mode there works
|
|
|
1353ca |
fine; since it's not a symlink, we'll just skip the extra
|
|
|
1353ca |
checks.
|
|
|
1353ca |
|
|
|
1353ca |
Signed-off-by: Jeff King <peff@peff.net>
|
|
|
1353ca |
---
|
|
|
1353ca |
builtin/apply.c | 4 ++--
|
|
|
1353ca |
builtin/update-index.c | 6 +++---
|
|
|
1353ca |
cache.h | 2 +-
|
|
|
1353ca |
read-cache.c | 40 +++++++++++++++++++++++++++++++---------
|
|
|
1353ca |
4 files changed, 37 insertions(+), 15 deletions(-)
|
|
|
1353ca |
|
|
|
1353ca |
diff --git a/builtin/apply.c b/builtin/apply.c
|
|
|
1353ca |
index c770d7d..8602b98 100644
|
|
|
1353ca |
--- a/builtin/apply.c
|
|
|
1353ca |
+++ b/builtin/apply.c
|
|
|
1353ca |
@@ -3683,9 +3683,9 @@ static void die_on_unsafe_path(struct patch *patch)
|
|
|
1353ca |
if (!patch->is_delete)
|
|
|
1353ca |
new_name = patch->new_name;
|
|
|
1353ca |
|
|
|
1353ca |
- if (old_name && !verify_path(old_name))
|
|
|
1353ca |
+ if (old_name && !verify_path(old_name, patch->old_mode))
|
|
|
1353ca |
die(_("invalid path '%s'"), old_name);
|
|
|
1353ca |
- if (new_name && !verify_path(new_name))
|
|
|
1353ca |
+ if (new_name && !verify_path(new_name, patch->new_mode))
|
|
|
1353ca |
die(_("invalid path '%s'"), new_name);
|
|
|
1353ca |
}
|
|
|
1353ca |
|
|
|
1353ca |
diff --git a/builtin/update-index.c b/builtin/update-index.c
|
|
|
1353ca |
index 0c5be84..c54bada 100644
|
|
|
1353ca |
--- a/builtin/update-index.c
|
|
|
1353ca |
+++ b/builtin/update-index.c
|
|
|
1353ca |
@@ -395,7 +395,7 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
|
|
|
1353ca |
int size, len, option;
|
|
|
1353ca |
struct cache_entry *ce;
|
|
|
1353ca |
|
|
|
1353ca |
- if (!verify_path(path))
|
|
|
1353ca |
+ if (!verify_path(path, mode))
|
|
|
1353ca |
return error("Invalid path '%s'", path);
|
|
|
1353ca |
|
|
|
1353ca |
len = strlen(path);
|
|
|
1353ca |
@@ -460,7 +460,7 @@ static void update_one(const char *path)
|
|
|
1353ca |
stat_errno = errno;
|
|
|
1353ca |
} /* else stat is valid */
|
|
|
1353ca |
|
|
|
1353ca |
- if (!verify_path(path)) {
|
|
|
1353ca |
+ if (!verify_path(path, st.st_mode)) {
|
|
|
1353ca |
fprintf(stderr, "Ignoring path %s\n", path);
|
|
|
1353ca |
return;
|
|
|
1353ca |
}
|
|
|
1353ca |
@@ -550,7 +550,7 @@ static void read_index_info(int nul_term_line)
|
|
|
1353ca |
path_name = uq.buf;
|
|
|
1353ca |
}
|
|
|
1353ca |
|
|
|
1353ca |
- if (!verify_path(path_name)) {
|
|
|
1353ca |
+ if (!verify_path(path_name, mode)) {
|
|
|
1353ca |
fprintf(stderr, "Ignoring path %s\n", path_name);
|
|
|
1353ca |
continue;
|
|
|
1353ca |
}
|
|
|
1353ca |
diff --git a/cache.h b/cache.h
|
|
|
1353ca |
index fbbe933..61d43c6 100644
|
|
|
1353ca |
--- a/cache.h
|
|
|
1353ca |
+++ b/cache.h
|
|
|
1353ca |
@@ -560,7 +560,7 @@ extern int read_index_unmerged(struct index_state *);
|
|
|
1353ca |
extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
|
|
|
1353ca |
extern int discard_index(struct index_state *);
|
|
|
1353ca |
extern int unmerged_index(const struct index_state *);
|
|
|
1353ca |
-extern int verify_path(const char *path);
|
|
|
1353ca |
+extern int verify_path(const char *path, unsigned mode);
|
|
|
1353ca |
extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
|
|
|
1353ca |
extern void adjust_dirname_case(struct index_state *istate, char *name);
|
|
|
1353ca |
extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
|
|
|
1353ca |
diff --git a/read-cache.c b/read-cache.c
|
|
|
1353ca |
index 88ed49c..d6dedd5 100644
|
|
|
1353ca |
--- a/read-cache.c
|
|
|
1353ca |
+++ b/read-cache.c
|
|
|
1353ca |
@@ -738,7 +738,7 @@ struct cache_entry *make_cache_entry(unsigned int mode,
|
|
|
1353ca |
int size, len;
|
|
|
1353ca |
struct cache_entry *ce, *ret;
|
|
|
1353ca |
|
|
|
1353ca |
- if (!verify_path(path)) {
|
|
|
1353ca |
+ if (!verify_path(path, mode)) {
|
|
|
1353ca |
error("Invalid path '%s'", path);
|
|
|
1353ca |
return NULL;
|
|
|
1353ca |
}
|
|
|
1353ca |
@@ -773,7 +773,7 @@ int ce_same_name(const struct cache_entry *a, const struct cache_entry *b)
|
|
|
1353ca |
* Also, we don't want double slashes or slashes at the
|
|
|
1353ca |
* end that can make pathnames ambiguous.
|
|
|
1353ca |
*/
|
|
|
1353ca |
-static int verify_dotfile(const char *rest)
|
|
|
1353ca |
+static int verify_dotfile(const char *rest, unsigned mode)
|
|
|
1353ca |
{
|
|
|
1353ca |
/*
|
|
|
1353ca |
* The first character was '.', but that
|
|
|
1353ca |
@@ -791,6 +791,9 @@ static int verify_dotfile(const char *rest)
|
|
|
1353ca |
* case-insensitively here, even if ignore_case is not set.
|
|
|
1353ca |
* This outlaws ".GIT" everywhere out of an abundance of caution,
|
|
|
1353ca |
* since there's really no good reason to allow it.
|
|
|
1353ca |
+ *
|
|
|
1353ca |
+ * Once we've seen ".git", we can also find ".gitmodules", etc (also
|
|
|
1353ca |
+ * case-insensitively).
|
|
|
1353ca |
*/
|
|
|
1353ca |
case 'g':
|
|
|
1353ca |
case 'G':
|
|
|
1353ca |
@@ -800,6 +803,12 @@ static int verify_dotfile(const char *rest)
|
|
|
1353ca |
break;
|
|
|
1353ca |
if (rest[3] == '\0' || is_dir_sep(rest[3]))
|
|
|
1353ca |
return 0;
|
|
|
1353ca |
+ if (S_ISLNK(mode)) {
|
|
|
1353ca |
+ rest += 3;
|
|
|
1353ca |
+ if (skip_iprefix(rest, "modules", &rest) &&
|
|
|
1353ca |
+ (*rest == '\0' || is_dir_sep(*rest)))
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+ }
|
|
|
1353ca |
break;
|
|
|
1353ca |
case '.':
|
|
|
1353ca |
if (rest[1] == '\0' || is_dir_sep(rest[1]))
|
|
|
1353ca |
@@ -808,7 +817,7 @@ static int verify_dotfile(const char *rest)
|
|
|
1353ca |
return 1;
|
|
|
1353ca |
}
|
|
|
1353ca |
|
|
|
1353ca |
-int verify_path(const char *path)
|
|
|
1353ca |
+int verify_path(const char *path, unsigned mode)
|
|
|
1353ca |
{
|
|
|
1353ca |
char c;
|
|
|
1353ca |
|
|
|
1353ca |
@@ -821,12 +830,25 @@ int verify_path(const char *path)
|
|
|
1353ca |
return 1;
|
|
|
1353ca |
if (is_dir_sep(c)) {
|
|
|
1353ca |
inside:
|
|
|
1353ca |
- if (protect_hfs && is_hfs_dotgit(path))
|
|
|
1353ca |
- return 0;
|
|
|
1353ca |
- if (protect_ntfs && is_ntfs_dotgit(path))
|
|
|
1353ca |
- return 0;
|
|
|
1353ca |
+ if (protect_hfs) {
|
|
|
1353ca |
+ if (is_hfs_dotgit(path))
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+ if (S_ISLNK(mode)) {
|
|
|
1353ca |
+ if (is_hfs_dotgitmodules(path))
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+ }
|
|
|
1353ca |
+ }
|
|
|
1353ca |
+ if (protect_ntfs) {
|
|
|
1353ca |
+ if (is_ntfs_dotgit(path))
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+ if (S_ISLNK(mode)) {
|
|
|
1353ca |
+ if (is_ntfs_dotgitmodules(path))
|
|
|
1353ca |
+ return 0;
|
|
|
1353ca |
+ }
|
|
|
1353ca |
+ }
|
|
|
1353ca |
+
|
|
|
1353ca |
c = *path++;
|
|
|
1353ca |
- if ((c == '.' && !verify_dotfile(path)) ||
|
|
|
1353ca |
+ if ((c == '.' && !verify_dotfile(path, mode)) ||
|
|
|
1353ca |
is_dir_sep(c) || c == '\0')
|
|
|
1353ca |
return 0;
|
|
|
1353ca |
}
|
|
|
1353ca |
@@ -1008,7 +1030,7 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
|
|
|
1353ca |
|
|
|
1353ca |
if (!ok_to_add)
|
|
|
1353ca |
return -1;
|
|
|
1353ca |
- if (!verify_path(ce->name))
|
|
|
1353ca |
+ if (!verify_path(ce->name, ce->ce_mode))
|
|
|
1353ca |
return error("Invalid path '%s'", ce->name);
|
|
|
1353ca |
|
|
|
1353ca |
if (!skip_df_check &&
|
|
|
1353ca |
--
|
|
|
1353ca |
2.14.4
|
|
|
1353ca |
|
|
|
1353ca |
|
|
|
1353ca |
From ab1977d8fdcf7b259e1a4ede2f99423dca97ea41 Mon Sep 17 00:00:00 2001
|
|
|
1353ca |
From: Sebastian Kisela <skisela@redhat.com>
|
|
|
1353ca |
Date: Wed, 20 Jun 2018 15:03:18 +0200
|
|
|
1353ca |
Subject: [PATCH 11/11] submodules: make t7415 test accept fail exit code
|
|
|
1353ca |
|
|
|
1353ca |
The command that performs the cloning fails with non zero exit
|
|
|
1353ca |
code, but the exit code itself is not important.
|
|
|
1353ca |
The command may fail but the general output must be as expected.
|
|
|
1353ca |
---
|
|
|
1353ca |
t/t7415-submodule-names.sh | 2 +-
|
|
|
1353ca |
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
|
1353ca |
|
|
|
1353ca |
diff --git a/t/t7415-submodule-names.sh b/t/t7415-submodule-names.sh
|
|
|
1353ca |
index 75fa071..b30fa33 100755
|
|
|
1353ca |
--- a/t/t7415-submodule-names.sh
|
|
|
1353ca |
+++ b/t/t7415-submodule-names.sh
|
|
|
1353ca |
@@ -69,7 +69,7 @@ test_expect_success 'add other submodule' '
|
|
|
1353ca |
'
|
|
|
1353ca |
|
|
|
1353ca |
test_expect_success 'clone evil superproject' '
|
|
|
1353ca |
- git clone --recurse-submodules . victim >output 2>&1 &&
|
|
|
1353ca |
+ test_might_fail git clone --recurse-submodules . victim >output 2>&1 &&
|
|
|
1353ca |
! grep "RUNNING POST CHECKOUT" output
|
|
|
1353ca |
'
|
|
|
1353ca |
|
|
|
1353ca |
--
|
|
|
1353ca |
2.14.4
|
|
|
1353ca |
|