Skip to content

Commit 416449e

Browse files
committed
Merge branch 'jk/symlinked-dotgitx-cleanup'
Various test and documentation updates about .gitsomething paths that are symlinks. * jk/symlinked-dotgitx-cleanup: docs: document symlink restrictions for dot-files fsck: warn about symlinked dotfiles we'll open with O_NOFOLLOW t0060: test ntfs/hfs-obscured dotfiles t7450: test .gitmodules symlink matching against obscured names t7450: test verify_path() handling of gitmodules t7415: rename to expand scope fsck_tree(): wrap some long lines fsck_tree(): fix shadowed variable t7415: remove out-dated comment about translation
2 parents 1af57f5 + 8ff06de commit 416449e

File tree

13 files changed

+255
-61
lines changed

13 files changed

+255
-61
lines changed

Documentation/gitattributes.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1247,6 +1247,12 @@ to:
12471247
[attr]binary -diff -merge -text
12481248
------------
12491249

1250+
NOTES
1251+
-----
1252+
1253+
Git does not follow symbolic links when accessing a `.gitattributes`
1254+
file in the working tree. This keeps behavior consistent when the file
1255+
is accessed from the index or a tree versus from the filesystem.
12501256

12511257
EXAMPLES
12521258
--------

Documentation/gitignore.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,10 @@ not tracked by Git remain untracked.
149149
To stop tracking a file that is currently tracked, use
150150
'git rm --cached'.
151151

152+
Git does not follow symbolic links when accessing a `.gitignore` file in
153+
the working tree. This keeps behavior consistent when the file is
154+
accessed from the index or a tree versus from the filesystem.
155+
152156
EXAMPLES
153157
--------
154158

Documentation/gitmailmap.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ this would also match the 'Commit Name <commit&#64;email.xx>' above:
5555
Proper Name <[email protected]> CoMmIt NaMe <[email protected]>
5656
--
5757

58+
NOTES
59+
-----
60+
61+
Git does not follow symbolic links when accessing a `.mailmap` file in
62+
the working tree. This keeps behavior consistent when the file is
63+
accessed from the index or a tree versus from the filesystem.
64+
5865
EXAMPLES
5966
--------
6067

Documentation/gitmodules.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@ submodule.<name>.shallow::
9898
shallow clone (with a history depth of 1) unless the user explicitly
9999
asks for a non-shallow clone.
100100

101+
NOTES
102+
-----
103+
104+
Git does not allow the `.gitmodules` file within a working tree to be a
105+
symbolic link, and will refuse to check out such a tree entry. This
106+
keeps behavior consistent when the file is accessed from the index or a
107+
tree versus from the filesystem, and helps Git reliably enforce security
108+
checks of the file contents.
101109

102110
EXAMPLES
103111
--------

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,6 +1272,7 @@ int is_ntfs_dotgit(const char *name);
12721272
int is_ntfs_dotgitmodules(const char *name);
12731273
int is_ntfs_dotgitignore(const char *name);
12741274
int is_ntfs_dotgitattributes(const char *name);
1275+
int is_ntfs_dotmailmap(const char *name);
12751276

12761277
/*
12771278
* Returns true iff "str" could be confused as a command-line option when

fsck.c

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ static int verify_ordered(unsigned mode1, const char *name1,
558558
return c1 < c2 ? 0 : TREE_UNORDERED;
559559
}
560560

561-
static int fsck_tree(const struct object_id *oid,
561+
static int fsck_tree(const struct object_id *tree_oid,
562562
const char *buffer, unsigned long size,
563563
struct fsck_options *options)
564564
{
@@ -579,7 +579,9 @@ static int fsck_tree(const struct object_id *oid,
579579
struct name_stack df_dup_candidates = { NULL };
580580

581581
if (init_tree_desc_gently(&desc, buffer, size)) {
582-
retval += report(options, oid, OBJ_TREE, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
582+
retval += report(options, tree_oid, OBJ_TREE,
583+
FSCK_MSG_BAD_TREE,
584+
"cannot be parsed as a tree");
583585
return retval;
584586
}
585587

@@ -589,11 +591,11 @@ static int fsck_tree(const struct object_id *oid,
589591
while (desc.size) {
590592
unsigned short mode;
591593
const char *name, *backslash;
592-
const struct object_id *oid;
594+
const struct object_id *entry_oid;
593595

594-
oid = tree_entry_extract(&desc, &name, &mode);
596+
entry_oid = tree_entry_extract(&desc, &name, &mode);
595597

596-
has_null_sha1 |= is_null_oid(oid);
598+
has_null_sha1 |= is_null_oid(entry_oid);
597599
has_full_path |= !!strchr(name, '/');
598600
has_empty_name |= !*name;
599601
has_dot |= !strcmp(name, ".");
@@ -603,23 +605,43 @@ static int fsck_tree(const struct object_id *oid,
603605

604606
if (is_hfs_dotgitmodules(name) || is_ntfs_dotgitmodules(name)) {
605607
if (!S_ISLNK(mode))
606-
oidset_insert(&options->gitmodules_found, oid);
608+
oidset_insert(&options->gitmodules_found,
609+
entry_oid);
607610
else
608611
retval += report(options,
609-
oid, OBJ_TREE,
612+
tree_oid, OBJ_TREE,
610613
FSCK_MSG_GITMODULES_SYMLINK,
611614
".gitmodules is a symbolic link");
612615
}
613616

617+
if (S_ISLNK(mode)) {
618+
if (is_hfs_dotgitignore(name) ||
619+
is_ntfs_dotgitignore(name))
620+
retval += report(options, tree_oid, OBJ_TREE,
621+
FSCK_MSG_GITIGNORE_SYMLINK,
622+
".gitignore is a symlink");
623+
if (is_hfs_dotgitattributes(name) ||
624+
is_ntfs_dotgitattributes(name))
625+
retval += report(options, tree_oid, OBJ_TREE,
626+
FSCK_MSG_GITATTRIBUTES_SYMLINK,
627+
".gitattributes is a symlink");
628+
if (is_hfs_dotmailmap(name) ||
629+
is_ntfs_dotmailmap(name))
630+
retval += report(options, tree_oid, OBJ_TREE,
631+
FSCK_MSG_MAILMAP_SYMLINK,
632+
".mailmap is a symlink");
633+
}
634+
614635
if ((backslash = strchr(name, '\\'))) {
615636
while (backslash) {
616637
backslash++;
617638
has_dotgit |= is_ntfs_dotgit(backslash);
618639
if (is_ntfs_dotgitmodules(backslash)) {
619640
if (!S_ISLNK(mode))
620-
oidset_insert(&options->gitmodules_found, oid);
641+
oidset_insert(&options->gitmodules_found,
642+
entry_oid);
621643
else
622-
retval += report(options, oid, OBJ_TREE,
644+
retval += report(options, tree_oid, OBJ_TREE,
623645
FSCK_MSG_GITMODULES_SYMLINK,
624646
".gitmodules is a symbolic link");
625647
}
@@ -628,7 +650,9 @@ static int fsck_tree(const struct object_id *oid,
628650
}
629651

630652
if (update_tree_entry_gently(&desc)) {
631-
retval += report(options, oid, OBJ_TREE, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
653+
retval += report(options, tree_oid, OBJ_TREE,
654+
FSCK_MSG_BAD_TREE,
655+
"cannot be parsed as a tree");
632656
break;
633657
}
634658

@@ -676,25 +700,45 @@ static int fsck_tree(const struct object_id *oid,
676700
name_stack_clear(&df_dup_candidates);
677701

678702
if (has_null_sha1)
679-
retval += report(options, oid, OBJ_TREE, FSCK_MSG_NULL_SHA1, "contains entries pointing to null sha1");
703+
retval += report(options, tree_oid, OBJ_TREE,
704+
FSCK_MSG_NULL_SHA1,
705+
"contains entries pointing to null sha1");
680706
if (has_full_path)
681-
retval += report(options, oid, OBJ_TREE, FSCK_MSG_FULL_PATHNAME, "contains full pathnames");
707+
retval += report(options, tree_oid, OBJ_TREE,
708+
FSCK_MSG_FULL_PATHNAME,
709+
"contains full pathnames");
682710
if (has_empty_name)
683-
retval += report(options, oid, OBJ_TREE, FSCK_MSG_EMPTY_NAME, "contains empty pathname");
711+
retval += report(options, tree_oid, OBJ_TREE,
712+
FSCK_MSG_EMPTY_NAME,
713+
"contains empty pathname");
684714
if (has_dot)
685-
retval += report(options, oid, OBJ_TREE, FSCK_MSG_HAS_DOT, "contains '.'");
715+
retval += report(options, tree_oid, OBJ_TREE,
716+
FSCK_MSG_HAS_DOT,
717+
"contains '.'");
686718
if (has_dotdot)
687-
retval += report(options, oid, OBJ_TREE, FSCK_MSG_HAS_DOTDOT, "contains '..'");
719+
retval += report(options, tree_oid, OBJ_TREE,
720+
FSCK_MSG_HAS_DOTDOT,
721+
"contains '..'");
688722
if (has_dotgit)
689-
retval += report(options, oid, OBJ_TREE, FSCK_MSG_HAS_DOTGIT, "contains '.git'");
723+
retval += report(options, tree_oid, OBJ_TREE,
724+
FSCK_MSG_HAS_DOTGIT,
725+
"contains '.git'");
690726
if (has_zero_pad)
691-
retval += report(options, oid, OBJ_TREE, FSCK_MSG_ZERO_PADDED_FILEMODE, "contains zero-padded file modes");
727+
retval += report(options, tree_oid, OBJ_TREE,
728+
FSCK_MSG_ZERO_PADDED_FILEMODE,
729+
"contains zero-padded file modes");
692730
if (has_bad_modes)
693-
retval += report(options, oid, OBJ_TREE, FSCK_MSG_BAD_FILEMODE, "contains bad file modes");
731+
retval += report(options, tree_oid, OBJ_TREE,
732+
FSCK_MSG_BAD_FILEMODE,
733+
"contains bad file modes");
694734
if (has_dup_entries)
695-
retval += report(options, oid, OBJ_TREE, FSCK_MSG_DUPLICATE_ENTRIES, "contains duplicate file entries");
735+
retval += report(options, tree_oid, OBJ_TREE,
736+
FSCK_MSG_DUPLICATE_ENTRIES,
737+
"contains duplicate file entries");
696738
if (not_properly_sorted)
697-
retval += report(options, oid, OBJ_TREE, FSCK_MSG_TREE_NOT_SORTED, "not properly sorted");
739+
retval += report(options, tree_oid, OBJ_TREE,
740+
FSCK_MSG_TREE_NOT_SORTED,
741+
"not properly sorted");
698742
return retval;
699743
}
700744

fsck.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ enum fsck_msg_type {
6767
FUNC(NUL_IN_COMMIT, WARN) \
6868
/* infos (reported as warnings, but ignored by default) */ \
6969
FUNC(GITMODULES_PARSE, INFO) \
70+
FUNC(GITIGNORE_SYMLINK, INFO) \
71+
FUNC(GITATTRIBUTES_SYMLINK, INFO) \
72+
FUNC(MAILMAP_SYMLINK, INFO) \
7073
FUNC(BAD_TAG_NAME, INFO) \
7174
FUNC(MISSING_TAGGER_ENTRY, INFO) \
7275
/* ignored (elevated when requested) */ \

path.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,6 +1493,11 @@ int is_ntfs_dotgitattributes(const char *name)
14931493
return is_ntfs_dot_str(name, "gitattributes", "gi7d29");
14941494
}
14951495

1496+
int is_ntfs_dotmailmap(const char *name)
1497+
{
1498+
return is_ntfs_dot_str(name, "mailmap", "maba30");
1499+
}
1500+
14961501
int looks_like_command_line_option(const char *str)
14971502
{
14981503
return str && str[0] == '-';

t/helper/test-path-utils.c

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -172,9 +172,22 @@ static struct test_data dirname_data[] = {
172172
{ NULL, NULL }
173173
};
174174

175-
static int is_dotgitmodules(const char *path)
175+
static int check_dotfile(const char *x, const char **argv,
176+
int (*is_hfs)(const char *),
177+
int (*is_ntfs)(const char *))
176178
{
177-
return is_hfs_dotgitmodules(path) || is_ntfs_dotgitmodules(path);
179+
int res = 0, expect = 1;
180+
for (; *argv; argv++) {
181+
if (!strcmp("--not", *argv))
182+
expect = !expect;
183+
else if (expect != (is_hfs(*argv) || is_ntfs(*argv)))
184+
res = error("'%s' is %s.git%s", *argv,
185+
expect ? "not " : "", x);
186+
else
187+
fprintf(stderr, "ok: '%s' is %s.git%s\n",
188+
*argv, expect ? "" : "not ", x);
189+
}
190+
return !!res;
178191
}
179192

180193
static int cmp_by_st_size(const void *a, const void *b)
@@ -382,17 +395,24 @@ int cmd__path_utils(int argc, const char **argv)
382395
return test_function(dirname_data, posix_dirname, argv[1]);
383396

384397
if (argc > 2 && !strcmp(argv[1], "is_dotgitmodules")) {
385-
int res = 0, expect = 1, i;
386-
for (i = 2; i < argc; i++)
387-
if (!strcmp("--not", argv[i]))
388-
expect = !expect;
389-
else if (expect != is_dotgitmodules(argv[i]))
390-
res = error("'%s' is %s.gitmodules", argv[i],
391-
expect ? "not " : "");
392-
else
393-
fprintf(stderr, "ok: '%s' is %s.gitmodules\n",
394-
argv[i], expect ? "" : "not ");
395-
return !!res;
398+
return check_dotfile("modules", argv + 2,
399+
is_hfs_dotgitmodules,
400+
is_ntfs_dotgitmodules);
401+
}
402+
if (argc > 2 && !strcmp(argv[1], "is_dotgitignore")) {
403+
return check_dotfile("ignore", argv + 2,
404+
is_hfs_dotgitignore,
405+
is_ntfs_dotgitignore);
406+
}
407+
if (argc > 2 && !strcmp(argv[1], "is_dotgitattributes")) {
408+
return check_dotfile("attributes", argv + 2,
409+
is_hfs_dotgitattributes,
410+
is_ntfs_dotgitattributes);
411+
}
412+
if (argc > 2 && !strcmp(argv[1], "is_dotmailmap")) {
413+
return check_dotfile("mailmap", argv + 2,
414+
is_hfs_dotmailmap,
415+
is_ntfs_dotmailmap);
396416
}
397417

398418
if (argc > 2 && !strcmp(argv[1], "file-size")) {

t/t0060-path-utils.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,36 @@ test_expect_success 'match .gitmodules' '
468468
.gitmodules,:\$DATA
469469
'
470470

471+
test_expect_success 'match .gitattributes' '
472+
test-tool path-utils is_dotgitattributes \
473+
.gitattributes \
474+
.git${u200c}attributes \
475+
.Gitattributes \
476+
.gitattributeS \
477+
GITATT~1 \
478+
GI7D29~1
479+
'
480+
481+
test_expect_success 'match .gitignore' '
482+
test-tool path-utils is_dotgitignore \
483+
.gitignore \
484+
.git${u200c}ignore \
485+
.Gitignore \
486+
.gitignorE \
487+
GITIGN~1 \
488+
GI250A~1
489+
'
490+
491+
test_expect_success 'match .mailmap' '
492+
test-tool path-utils is_dotmailmap \
493+
.mailmap \
494+
.mail${u200c}map \
495+
.Mailmap \
496+
.mailmaP \
497+
MAILMA~1 \
498+
MABA30~1
499+
'
500+
471501
test_expect_success MINGW 'is_valid_path() on Windows' '
472502
test-tool path-utils is_valid_path \
473503
win32 \

0 commit comments

Comments
 (0)