Skip to content

Commit 204333b

Browse files
committed
Merge branch 'jk/open-dotgitx-with-nofollow'
It does not make sense to make ".gitattributes", ".gitignore" and ".mailmap" symlinks, as they are supposed to be usable from the object store (think: bare repositories where HEAD:.mailmap etc. are used). When these files are symbolic links, we used to read the contents of the files pointed by them by mistake, which has been corrected. * jk/open-dotgitx-with-nofollow: mailmap: do not respect symlinks for in-tree .mailmap exclude: do not respect symlinks for in-tree .gitignore attr: do not respect symlinks for in-tree .gitattributes exclude: add flags parameter to add_patterns() attr: convert "macro_ok" into a flags field add open_nofollow() helper
2 parents 98164e9 + adcd9f5 commit 204333b

File tree

10 files changed

+197
-41
lines changed

10 files changed

+197
-41
lines changed

attr.c

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,10 @@ struct match_attr {
278278

279279
static const char blank[] = " \t\r\n";
280280

281+
/* Flags usable in read_attr() and parse_attr_line() family of functions. */
282+
#define READ_ATTR_MACRO_OK (1<<0)
283+
#define READ_ATTR_NOFOLLOW (1<<1)
284+
281285
/*
282286
* Parse a whitespace-delimited attribute state (i.e., "attr",
283287
* "-attr", "!attr", or "attr=value") from the string starting at src.
@@ -331,7 +335,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
331335
}
332336

333337
static struct match_attr *parse_attr_line(const char *line, const char *src,
334-
int lineno, int macro_ok)
338+
int lineno, unsigned flags)
335339
{
336340
int namelen;
337341
int num_attr, i;
@@ -355,7 +359,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
355359

356360
if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen &&
357361
starts_with(name, ATTRIBUTE_MACRO_PREFIX)) {
358-
if (!macro_ok) {
362+
if (!(flags & READ_ATTR_MACRO_OK)) {
359363
fprintf_ln(stderr, _("%s not allowed: %s:%d"),
360364
name, src, lineno);
361365
goto fail_return;
@@ -653,11 +657,11 @@ static void handle_attr_line(struct attr_stack *res,
653657
const char *line,
654658
const char *src,
655659
int lineno,
656-
int macro_ok)
660+
unsigned flags)
657661
{
658662
struct match_attr *a;
659663

660-
a = parse_attr_line(line, src, lineno, macro_ok);
664+
a = parse_attr_line(line, src, lineno, flags);
661665
if (!a)
662666
return;
663667
ALLOC_GROW(res->attrs, res->num_matches + 1, res->alloc);
@@ -672,7 +676,8 @@ static struct attr_stack *read_attr_from_array(const char **list)
672676

673677
CALLOC_ARRAY(res, 1);
674678
while ((line = *(list++)) != NULL)
675-
handle_attr_line(res, line, "[builtin]", ++lineno, 1);
679+
handle_attr_line(res, line, "[builtin]", ++lineno,
680+
READ_ATTR_MACRO_OK);
676681
return res;
677682
}
678683

@@ -698,29 +703,39 @@ void git_attr_set_direction(enum git_attr_direction new_direction)
698703
direction = new_direction;
699704
}
700705

701-
static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
706+
static struct attr_stack *read_attr_from_file(const char *path, unsigned flags)
702707
{
703-
FILE *fp = fopen_or_warn(path, "r");
708+
int fd;
709+
FILE *fp;
704710
struct attr_stack *res;
705711
char buf[2048];
706712
int lineno = 0;
707713

708-
if (!fp)
714+
if (flags & READ_ATTR_NOFOLLOW)
715+
fd = open_nofollow(path, O_RDONLY);
716+
else
717+
fd = open(path, O_RDONLY);
718+
719+
if (fd < 0) {
720+
warn_on_fopen_errors(path);
709721
return NULL;
722+
}
723+
fp = xfdopen(fd, "r");
724+
710725
CALLOC_ARRAY(res, 1);
711726
while (fgets(buf, sizeof(buf), fp)) {
712727
char *bufp = buf;
713728
if (!lineno)
714729
skip_utf8_bom(&bufp, strlen(bufp));
715-
handle_attr_line(res, bufp, path, ++lineno, macro_ok);
730+
handle_attr_line(res, bufp, path, ++lineno, flags);
716731
}
717732
fclose(fp);
718733
return res;
719734
}
720735

721736
static struct attr_stack *read_attr_from_index(const struct index_state *istate,
722737
const char *path,
723-
int macro_ok)
738+
unsigned flags)
724739
{
725740
struct attr_stack *res;
726741
char *buf, *sp;
@@ -741,35 +756,35 @@ static struct attr_stack *read_attr_from_index(const struct index_state *istate,
741756
ep = strchrnul(sp, '\n');
742757
more = (*ep == '\n');
743758
*ep = '\0';
744-
handle_attr_line(res, sp, path, ++lineno, macro_ok);
759+
handle_attr_line(res, sp, path, ++lineno, flags);
745760
sp = ep + more;
746761
}
747762
free(buf);
748763
return res;
749764
}
750765

751766
static struct attr_stack *read_attr(const struct index_state *istate,
752-
const char *path, int macro_ok)
767+
const char *path, unsigned flags)
753768
{
754769
struct attr_stack *res = NULL;
755770

756771
if (direction == GIT_ATTR_INDEX) {
757-
res = read_attr_from_index(istate, path, macro_ok);
772+
res = read_attr_from_index(istate, path, flags);
758773
} else if (!is_bare_repository()) {
759774
if (direction == GIT_ATTR_CHECKOUT) {
760-
res = read_attr_from_index(istate, path, macro_ok);
775+
res = read_attr_from_index(istate, path, flags);
761776
if (!res)
762-
res = read_attr_from_file(path, macro_ok);
777+
res = read_attr_from_file(path, flags);
763778
} else if (direction == GIT_ATTR_CHECKIN) {
764-
res = read_attr_from_file(path, macro_ok);
779+
res = read_attr_from_file(path, flags);
765780
if (!res)
766781
/*
767782
* There is no checked out .gitattributes file
768783
* there, but we might have it in the index.
769784
* We allow operation in a sparsely checked out
770785
* work tree, so read from it.
771786
*/
772-
res = read_attr_from_index(istate, path, macro_ok);
787+
res = read_attr_from_index(istate, path, flags);
773788
}
774789
}
775790

@@ -844,6 +859,7 @@ static void bootstrap_attr_stack(const struct index_state *istate,
844859
struct attr_stack **stack)
845860
{
846861
struct attr_stack *e;
862+
unsigned flags = READ_ATTR_MACRO_OK;
847863

848864
if (*stack)
849865
return;
@@ -854,23 +870,23 @@ static void bootstrap_attr_stack(const struct index_state *istate,
854870

855871
/* system-wide frame */
856872
if (git_attr_system()) {
857-
e = read_attr_from_file(git_etc_gitattributes(), 1);
873+
e = read_attr_from_file(git_etc_gitattributes(), flags);
858874
push_stack(stack, e, NULL, 0);
859875
}
860876

861877
/* home directory */
862878
if (get_home_gitattributes()) {
863-
e = read_attr_from_file(get_home_gitattributes(), 1);
879+
e = read_attr_from_file(get_home_gitattributes(), flags);
864880
push_stack(stack, e, NULL, 0);
865881
}
866882

867883
/* root directory */
868-
e = read_attr(istate, GITATTRIBUTES_FILE, 1);
884+
e = read_attr(istate, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW);
869885
push_stack(stack, e, xstrdup(""), 0);
870886

871887
/* info frame */
872888
if (startup_info->have_repository)
873-
e = read_attr_from_file(git_path_info_attributes(), 1);
889+
e = read_attr_from_file(git_path_info_attributes(), flags);
874890
else
875891
e = NULL;
876892
if (!e)
@@ -956,7 +972,7 @@ static void prepare_attr_stack(const struct index_state *istate,
956972
strbuf_add(&pathbuf, path + pathbuf.len, (len - pathbuf.len));
957973
strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE);
958974

959-
next = read_attr(istate, pathbuf.buf, 0);
975+
next = read_attr(istate, pathbuf.buf, READ_ATTR_NOFOLLOW);
960976

961977
/* reset the pathbuf to not include "/.gitattributes" */
962978
strbuf_setlen(&pathbuf, len);

builtin/sparse-checkout.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ static int sparse_checkout_list(int argc, const char **argv)
6464
pl.use_cone_patterns = core_sparse_checkout_cone;
6565

6666
sparse_filename = get_sparse_checkout_filename();
67-
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
67+
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL, 0);
6868
free(sparse_filename);
6969

7070
if (res < 0) {
@@ -321,7 +321,7 @@ static int sparse_checkout_init(int argc, const char **argv)
321321
memset(&pl, 0, sizeof(pl));
322322

323323
sparse_filename = get_sparse_checkout_filename();
324-
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
324+
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL, 0);
325325

326326
/* If we already have a sparse-checkout file, use it. */
327327
if (res >= 0) {
@@ -483,7 +483,7 @@ static void add_patterns_cone_mode(int argc, const char **argv,
483483
existing.use_cone_patterns = core_sparse_checkout_cone;
484484

485485
if (add_patterns_from_file_to_list(sparse_filename, "", 0,
486-
&existing, NULL))
486+
&existing, NULL, 0))
487487
die(_("unable to load existing sparse-checkout patterns"));
488488
free(sparse_filename);
489489

@@ -507,7 +507,7 @@ static void add_patterns_literal(int argc, const char **argv,
507507
{
508508
char *sparse_filename = get_sparse_checkout_filename();
509509
if (add_patterns_from_file_to_list(sparse_filename, "", 0,
510-
pl, NULL))
510+
pl, NULL, 0))
511511
die(_("unable to load existing sparse-checkout patterns"));
512512
free(sparse_filename);
513513
add_patterns_from_input(pl, argc, argv);

dir.c

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,9 @@ static int add_patterns_from_buffer(char *buf, size_t size,
10351035
const char *base, int baselen,
10361036
struct pattern_list *pl);
10371037

1038+
/* Flags for add_patterns() */
1039+
#define PATTERN_NOFOLLOW (1<<0)
1040+
10381041
/*
10391042
* Given a file with name "fname", read it (either from disk, or from
10401043
* an index if 'istate' is non-null), parse it and store the
@@ -1046,15 +1049,19 @@ static int add_patterns_from_buffer(char *buf, size_t size,
10461049
*/
10471050
static int add_patterns(const char *fname, const char *base, int baselen,
10481051
struct pattern_list *pl, struct index_state *istate,
1049-
struct oid_stat *oid_stat)
1052+
unsigned flags, struct oid_stat *oid_stat)
10501053
{
10511054
struct stat st;
10521055
int r;
10531056
int fd;
10541057
size_t size = 0;
10551058
char *buf;
10561059

1057-
fd = open(fname, O_RDONLY);
1060+
if (flags & PATTERN_NOFOLLOW)
1061+
fd = open_nofollow(fname, O_RDONLY);
1062+
else
1063+
fd = open(fname, O_RDONLY);
1064+
10581065
if (fd < 0 || fstat(fd, &st) < 0) {
10591066
if (fd < 0)
10601067
warn_on_fopen_errors(fname);
@@ -1143,9 +1150,10 @@ static int add_patterns_from_buffer(char *buf, size_t size,
11431150

11441151
int add_patterns_from_file_to_list(const char *fname, const char *base,
11451152
int baselen, struct pattern_list *pl,
1146-
struct index_state *istate)
1153+
struct index_state *istate,
1154+
unsigned flags)
11471155
{
1148-
return add_patterns(fname, base, baselen, pl, istate, NULL);
1156+
return add_patterns(fname, base, baselen, pl, istate, flags, NULL);
11491157
}
11501158

11511159
int add_patterns_from_blob_to_list(
@@ -1194,7 +1202,7 @@ static void add_patterns_from_file_1(struct dir_struct *dir, const char *fname,
11941202
if (!dir->untracked)
11951203
dir->unmanaged_exclude_files++;
11961204
pl = add_pattern_list(dir, EXC_FILE, fname);
1197-
if (add_patterns(fname, "", 0, pl, NULL, oid_stat) < 0)
1205+
if (add_patterns(fname, "", 0, pl, NULL, 0, oid_stat) < 0)
11981206
die(_("cannot use %s as an exclude file"), fname);
11991207
}
12001208

@@ -1558,6 +1566,7 @@ static void prep_exclude(struct dir_struct *dir,
15581566
strbuf_addstr(&sb, dir->exclude_per_dir);
15591567
pl->src = strbuf_detach(&sb, NULL);
15601568
add_patterns(pl->src, pl->src, stk->baselen, pl, istate,
1569+
PATTERN_NOFOLLOW,
15611570
untracked ? &oid_stat : NULL);
15621571
}
15631572
/*
@@ -3006,7 +3015,7 @@ int get_sparse_checkout_patterns(struct pattern_list *pl)
30063015
char *sparse_filename = get_sparse_checkout_filename();
30073016

30083017
pl->use_cone_patterns = core_sparse_checkout_cone;
3009-
res = add_patterns_from_file_to_list(sparse_filename, "", 0, pl, NULL);
3018+
res = add_patterns_from_file_to_list(sparse_filename, "", 0, pl, NULL, 0);
30103019

30113020
free(sparse_filename);
30123021
return res;

dir.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,8 @@ int hashmap_contains_parent(struct hashmap *map,
420420
struct pattern_list *add_pattern_list(struct dir_struct *dir,
421421
int group_type, const char *src);
422422
int add_patterns_from_file_to_list(const char *fname, const char *base, int baselen,
423-
struct pattern_list *pl, struct index_state *istate);
423+
struct pattern_list *pl, struct index_state *istate,
424+
unsigned flags);
424425
void add_patterns_from_file(struct dir_struct *, const char *fname);
425426
int add_patterns_from_blob_to_list(struct object_id *oid,
426427
const char *base, int baselen,

git-compat-util.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,13 @@ int access_or_die(const char *path, int mode, unsigned flag);
12421242
/* Warn on an inaccessible file if errno indicates this is an error */
12431243
int warn_on_fopen_errors(const char *path);
12441244

1245+
/*
1246+
* Open with O_NOFOLLOW, or equivalent. Note that the fallback equivalent
1247+
* may be racy. Do not use this as protection against an attacker who can
1248+
* simultaneously create paths.
1249+
*/
1250+
int open_nofollow(const char *path, int flags);
1251+
12451252
#if !defined(USE_PARENS_AROUND_GETTEXT_N) && defined(__GNUC__)
12461253
#define USE_PARENS_AROUND_GETTEXT_N 1
12471254
#endif

mailmap.c

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,20 +157,30 @@ static void read_mailmap_line(struct string_list *map, char *buffer)
157157
add_mapping(map, name1, email1, name2, email2);
158158
}
159159

160-
static int read_mailmap_file(struct string_list *map, const char *filename)
160+
/* Flags for read_mailmap_file() */
161+
#define MAILMAP_NOFOLLOW (1<<0)
162+
163+
static int read_mailmap_file(struct string_list *map, const char *filename,
164+
unsigned flags)
161165
{
162166
char buffer[1024];
163167
FILE *f;
168+
int fd;
164169

165170
if (!filename)
166171
return 0;
167172

168-
f = fopen(filename, "r");
169-
if (!f) {
173+
if (flags & MAILMAP_NOFOLLOW)
174+
fd = open_nofollow(filename, O_RDONLY);
175+
else
176+
fd = open(filename, O_RDONLY);
177+
178+
if (fd < 0) {
170179
if (errno == ENOENT)
171180
return 0;
172181
return error_errno("unable to open mailmap at %s", filename);
173182
}
183+
f = xfdopen(fd, "r");
174184

175185
while (fgets(buffer, sizeof(buffer), f) != NULL)
176186
read_mailmap_line(map, buffer);
@@ -226,10 +236,12 @@ int read_mailmap(struct string_list *map)
226236
git_mailmap_blob = "HEAD:.mailmap";
227237

228238
if (!startup_info->have_repository || !is_bare_repository())
229-
err |= read_mailmap_file(map, ".mailmap");
239+
err |= read_mailmap_file(map, ".mailmap",
240+
startup_info->have_repository ?
241+
MAILMAP_NOFOLLOW : 0);
230242
if (startup_info->have_repository)
231243
err |= read_mailmap_blob(map, git_mailmap_blob);
232-
err |= read_mailmap_file(map, git_mailmap_file);
244+
err |= read_mailmap_file(map, git_mailmap_file, 0);
233245
return err;
234246
}
235247

0 commit comments

Comments
 (0)