Skip to content

Commit 5e519fb

Browse files
committed
Sync with v1.9.5
* maint-1.9: Git 1.9.5 Git 1.8.5.6 fsck: complain about NTFS ".git" aliases in trees read-cache: optionally disallow NTFS .git variants path: add is_ntfs_dotgit() helper fsck: complain about HFS+ ".git" aliases in trees read-cache: optionally disallow HFS+ .git variants utf8: add is_hfs_dotgit() helper fsck: notice .git case-insensitively t1450: refactor ".", "..", and ".git" fsck tests verify_dotfile(): reject .git case-insensitively read-tree: add tests for confusing paths like ".." and ".git" unpack-trees: propagate errors adding entries to the index
2 parents 76f8611 + 8333263 commit 5e519fb

17 files changed

+334
-40
lines changed

Documentation/RelNotes/1.8.5.6.txt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Git v1.8.5.6 Release Notes
2+
==========================
3+
4+
Fixes since v1.8.5.5
5+
--------------------
6+
7+
* We used to allow committing a path ".Git/config" with Git that is
8+
running on a case sensitive filesystem, but an attempt to check out
9+
such a path with Git that runs on a case insensitive filesystem
10+
would have clobbered ".git/config", which is definitely not what
11+
the user would have expected. Git now prevents you from tracking
12+
a path with ".Git" (in any case combination) as a path component.
13+
14+
* On Windows, certain path components that are different from ".git"
15+
are mapped to ".git", e.g. "git~1/config" is treated as if it were
16+
".git/config". HFS+ has a similar issue, where certain unicode
17+
codepoints are ignored, e.g. ".g\u200cit/config" is treated as if
18+
it were ".git/config". Pathnames with these potential issues are
19+
rejected on the affected systems. Git on systems that are not
20+
affected by this issue (e.g. Linux) can also be configured to
21+
reject them to ensure cross platform interoperability of the hosted
22+
projects.
23+
24+
* "git fsck" notices a tree object that records such a path that can
25+
be confused with ".git", and with receive.fsckObjects configuration
26+
set to true, an attempt to "git push" such a tree object will be
27+
rejected. Such a path may not be a problem on a well behaving
28+
filesystem but in order to protect those on HFS+ and on case
29+
insensitive filesystems, this check is enabled on all platforms.
30+
31+
A big "thanks!" for bringing this issue to us goes to our friends in
32+
the Mercurial land, namely, Matt Mackall and Augie Fackler.
33+
34+
Also contains typofixes, documentation updates and trivial code clean-ups.

Documentation/RelNotes/1.9.5.txt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Git v1.9.5 Release Notes
2+
========================
3+
4+
Fixes since v1.9.4
5+
------------------
6+
7+
* We used to allow committing a path ".Git/config" with Git that is
8+
running on a case sensitive filesystem, but an attempt to check out
9+
such a path with Git that runs on a case insensitive filesystem
10+
would have clobbered ".git/config", which is definitely not what
11+
the user would have expected. Git now prevents you from tracking
12+
a path with ".Git" (in any case combination) as a path component.
13+
14+
* On Windows, certain path components that are different from ".git"
15+
are mapped to ".git", e.g. "git~1/config" is treated as if it were
16+
".git/config". HFS+ has a similar issue, where certain unicode
17+
codepoints are ignored, e.g. ".g\u200cit/config" is treated as if
18+
it were ".git/config". Pathnames with these potential issues are
19+
rejected on the affected systems. Git on systems that are not
20+
affected by this issue (e.g. Linux) can also be configured to
21+
reject them to ensure cross platform interoperability of the hosted
22+
projects.
23+
24+
* "git fsck" notices a tree object that records such a path that can
25+
be confused with ".git", and with receive.fsckObjects configuration
26+
set to true, an attempt to "git push" such a tree object will be
27+
rejected. Such a path may not be a problem on a well behaving
28+
filesystem but in order to protect those on HFS+ and on case
29+
insensitive filesystems, this check is enabled on all platforms.
30+
31+
A big "thanks!" for bringing this issue to us goes to our friends in
32+
the Mercurial land, namely, Matt Mackall and Augie Fackler.
33+
34+
Also contains typofixes, documentation updates and trivial code clean-ups.

Documentation/config.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,17 @@ core.precomposeunicode::
233233
When false, file names are handled fully transparent by Git,
234234
which is backward compatible with older versions of Git.
235235

236+
core.protectHFS::
237+
If set to true, do not allow checkout of paths that would
238+
be considered equivalent to `.git` on an HFS+ filesystem.
239+
Defaults to `true` on Mac OS, and `false` elsewhere.
240+
241+
core.protectNTFS::
242+
If set to true, do not allow checkout of paths that would
243+
cause problems with the NTFS filesystem, e.g. conflict with
244+
8.3 "short" names.
245+
Defaults to `true` on Windows, and `false` elsewhere.
246+
236247
core.trustctime::
237248
If false, the ctime differences between the index and the
238249
working tree are ignored; useful when the inode change time

Documentation/git.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,20 @@ Documentation for older releases are available here:
5252
link:RelNotes/2.0.1.txt[2.0.1],
5353
link:RelNotes/2.0.0.txt[2.0.0].
5454

55-
* link:v1.9.4/git.html[documentation for release 1.9.4]
55+
* link:v1.9.5/git.html[documentation for release 1.9.5]
5656

5757
* release notes for
58+
link:RelNotes/1.9.5.txt[1.9.5],
5859
link:RelNotes/1.9.4.txt[1.9.4],
5960
link:RelNotes/1.9.3.txt[1.9.3],
6061
link:RelNotes/1.9.2.txt[1.9.2],
6162
link:RelNotes/1.9.1.txt[1.9.1],
6263
link:RelNotes/1.9.0.txt[1.9.0].
6364

64-
* link:v1.8.5.5/git.html[documentation for release 1.8.5.5]
65+
* link:v1.8.5.6/git.html[documentation for release 1.8.5.6]
6566

6667
* release notes for
68+
link:RelNotes/1.8.5.6.txt[1.8.5.6],
6769
link:RelNotes/1.8.5.5.txt[1.8.5.5],
6870
link:RelNotes/1.8.5.4.txt[1.8.5.4],
6971
link:RelNotes/1.8.5.3.txt[1.8.5.3],

cache.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,8 @@ extern int fsync_object_files;
597597
extern int core_preload_index;
598598
extern int core_apply_sparse_checkout;
599599
extern int precomposed_unicode;
600+
extern int protect_hfs;
601+
extern int protect_ntfs;
600602

601603
/*
602604
* The character that begins a commented line in user-editable file
@@ -811,6 +813,7 @@ int longest_ancestor_length(const char *path, struct string_list *prefixes);
811813
char *strip_path_suffix(const char *path, const char *suffix);
812814
int daemon_avoid_alias(const char *path);
813815
int offset_1st_component(const char *path);
816+
extern int is_ntfs_dotgit(const char *name);
814817

815818
/* object replacement */
816819
#define LOOKUP_REPLACE_OBJECT 1

config.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -874,6 +874,16 @@ static int git_default_core_config(const char *var, const char *value)
874874
return 0;
875875
}
876876

877+
if (!strcmp(var, "core.protecthfs")) {
878+
protect_hfs = git_config_bool(var, value);
879+
return 0;
880+
}
881+
882+
if (!strcmp(var, "core.protectntfs")) {
883+
protect_ntfs = git_config_bool(var, value);
884+
return 0;
885+
}
886+
877887
/* Add other config variables here and to Documentation/config.txt. */
878888
return 0;
879889
}

config.mak.uname

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ ifeq ($(uname_S),Darwin)
9797
HAVE_DEV_TTY = YesPlease
9898
COMPAT_OBJS += compat/precompose_utf8.o
9999
BASIC_CFLAGS += -DPRECOMPOSE_UNICODE
100+
BASIC_CFLAGS += -DPROTECT_HFS_DEFAULT=1
100101
endif
101102
ifeq ($(uname_S),SunOS)
102103
NEEDS_SOCKET = YesPlease
@@ -361,6 +362,7 @@ ifeq ($(uname_S),Windows)
361362
EXTLIBS = user32.lib advapi32.lib shell32.lib wininet.lib ws2_32.lib invalidcontinue.obj
362363
PTHREAD_LIBS =
363364
lib =
365+
BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1
364366
ifndef DEBUG
365367
BASIC_CFLAGS += -GL -Os -MT
366368
BASIC_LDFLAGS += -LTCG
@@ -501,6 +503,7 @@ ifneq (,$(findstring MINGW,$(uname_S)))
501503
COMPAT_OBJS += compat/mingw.o compat/winansi.o \
502504
compat/win32/pthread.o compat/win32/syslog.o \
503505
compat/win32/dirent.o
506+
BASIC_CFLAGS += -DPROTECT_NTFS_DEFAULT=1
504507
BASIC_LDFLAGS += -Wl,--large-address-aware
505508
EXTLIBS += -lws2_32
506509
GITLIBS += git.res

environment.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,16 @@ int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */
6464
struct startup_info *startup_info;
6565
unsigned long pack_size_limit_cfg;
6666

67+
#ifndef PROTECT_HFS_DEFAULT
68+
#define PROTECT_HFS_DEFAULT 0
69+
#endif
70+
int protect_hfs = PROTECT_HFS_DEFAULT;
71+
72+
#ifndef PROTECT_NTFS_DEFAULT
73+
#define PROTECT_NTFS_DEFAULT 0
74+
#endif
75+
int protect_ntfs = PROTECT_NTFS_DEFAULT;
76+
6777
/*
6878
* The character that begins a commented line in user-editable file
6979
* that is subject to stripspace.

fsck.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "commit.h"
77
#include "tag.h"
88
#include "fsck.h"
9+
#include "utf8.h"
910

1011
static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data)
1112
{
@@ -170,7 +171,9 @@ static int fsck_tree(struct tree *item, int strict, fsck_error error_func)
170171
has_empty_name |= !*name;
171172
has_dot |= !strcmp(name, ".");
172173
has_dotdot |= !strcmp(name, "..");
173-
has_dotgit |= !strcmp(name, ".git");
174+
has_dotgit |= (!strcmp(name, ".git") ||
175+
is_hfs_dotgit(name) ||
176+
is_ntfs_dotgit(name));
174177
has_zero_pad |= *(char *)desc.buffer == '0';
175178
update_tree_entry(&desc);
176179

path.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -828,3 +828,36 @@ int offset_1st_component(const char *path)
828828
return 2 + is_dir_sep(path[2]);
829829
return is_dir_sep(path[0]);
830830
}
831+
832+
static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
833+
{
834+
if (len < skip)
835+
return 0;
836+
len -= skip;
837+
path += skip;
838+
while (len-- > 0) {
839+
char c = *(path++);
840+
if (c != ' ' && c != '.')
841+
return 0;
842+
}
843+
return 1;
844+
}
845+
846+
int is_ntfs_dotgit(const char *name)
847+
{
848+
int len;
849+
850+
for (len = 0; ; len++)
851+
if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) {
852+
if (only_spaces_and_periods(name, len, 4) &&
853+
!strncasecmp(name, ".git", 4))
854+
return 1;
855+
if (only_spaces_and_periods(name, len, 5) &&
856+
!strncasecmp(name, "git~1", 5))
857+
return 1;
858+
if (name[len] != '\\')
859+
return 0;
860+
name += len + 1;
861+
len = -1;
862+
}
863+
}

read-cache.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "resolve-undo.h"
1515
#include "strbuf.h"
1616
#include "varint.h"
17+
#include "utf8.h"
1718

1819
static struct cache_entry *refresh_cache_entry(struct cache_entry *ce,
1920
unsigned int options);
@@ -756,9 +757,10 @@ static int verify_dotfile(const char *rest)
756757
* shares the path end test with the ".." case.
757758
*/
758759
case 'g':
759-
if (rest[1] != 'i')
760+
case 'G':
761+
if (rest[1] != 'i' && rest[1] != 'I')
760762
break;
761-
if (rest[2] != 't')
763+
if (rest[2] != 't' && rest[2] != 'T')
762764
break;
763765
rest += 2;
764766
/* fallthrough */
@@ -782,6 +784,10 @@ int verify_path(const char *path)
782784
return 1;
783785
if (is_dir_sep(c)) {
784786
inside:
787+
if (protect_hfs && is_hfs_dotgit(path))
788+
return 0;
789+
if (protect_ntfs && is_ntfs_dotgit(path))
790+
return 0;
785791
c = *path++;
786792
if ((c == '.' && !verify_dotfile(path)) ||
787793
is_dir_sep(c) || c == '\0')

t/t1014-read-tree-confusing.sh

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/bin/sh
2+
3+
test_description='check that read-tree rejects confusing paths'
4+
. ./test-lib.sh
5+
6+
test_expect_success 'create base tree' '
7+
echo content >file &&
8+
git add file &&
9+
git commit -m base &&
10+
blob=$(git rev-parse HEAD:file) &&
11+
tree=$(git rev-parse HEAD^{tree})
12+
'
13+
14+
test_expect_success 'enable core.protectHFS for rejection tests' '
15+
git config core.protectHFS true
16+
'
17+
18+
test_expect_success 'enable core.protectNTFS for rejection tests' '
19+
git config core.protectNTFS true
20+
'
21+
22+
while read path pretty; do
23+
: ${pretty:=$path}
24+
case "$path" in
25+
*SPACE)
26+
path="${path%SPACE} "
27+
;;
28+
esac
29+
test_expect_success "reject $pretty at end of path" '
30+
printf "100644 blob %s\t%s" "$blob" "$path" >tree &&
31+
bogus=$(git mktree <tree) &&
32+
test_must_fail git read-tree $bogus
33+
'
34+
35+
test_expect_success "reject $pretty as subtree" '
36+
printf "040000 tree %s\t%s" "$tree" "$path" >tree &&
37+
bogus=$(git mktree <tree) &&
38+
test_must_fail git read-tree $bogus
39+
'
40+
done <<-EOF
41+
.
42+
..
43+
.git
44+
.GIT
45+
${u200c}.Git {u200c}.Git
46+
.gI${u200c}T .gI{u200c}T
47+
.GiT${u200c} .GiT{u200c}
48+
git~1
49+
.git.SPACE .git.{space}
50+
.\\\\.GIT\\\\foobar backslashes
51+
.git\\\\foobar backslashes2
52+
EOF
53+
54+
test_expect_success 'utf-8 paths allowed with core.protectHFS off' '
55+
test_when_finished "git read-tree HEAD" &&
56+
test_config core.protectHFS false &&
57+
printf "100644 blob %s\t%s" "$blob" ".gi${u200c}t" >tree &&
58+
ok=$(git mktree <tree) &&
59+
git read-tree $ok
60+
'
61+
62+
test_done

0 commit comments

Comments
 (0)