Skip to content

Commit

Permalink
Merge branch 'jk/open-dotgitx-with-nofollow'
Browse files Browse the repository at this point in the history
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
  • Loading branch information
gitster committed Mar 22, 2021
2 parents 98164e9 + adcd9f5 commit 204333b
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 41 deletions.
60 changes: 38 additions & 22 deletions attr.c
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,10 @@ struct match_attr {

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

/* Flags usable in read_attr() and parse_attr_line() family of functions. */
#define READ_ATTR_MACRO_OK (1<<0)
#define READ_ATTR_NOFOLLOW (1<<1)

/*
* Parse a whitespace-delimited attribute state (i.e., "attr",
* "-attr", "!attr", or "attr=value") from the string starting at src.
Expand Down Expand Up @@ -331,7 +335,7 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
}

static struct match_attr *parse_attr_line(const char *line, const char *src,
int lineno, int macro_ok)
int lineno, unsigned flags)
{
int namelen;
int num_attr, i;
Expand All @@ -355,7 +359,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,

if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen &&
starts_with(name, ATTRIBUTE_MACRO_PREFIX)) {
if (!macro_ok) {
if (!(flags & READ_ATTR_MACRO_OK)) {
fprintf_ln(stderr, _("%s not allowed: %s:%d"),
name, src, lineno);
goto fail_return;
Expand Down Expand Up @@ -653,11 +657,11 @@ static void handle_attr_line(struct attr_stack *res,
const char *line,
const char *src,
int lineno,
int macro_ok)
unsigned flags)
{
struct match_attr *a;

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

CALLOC_ARRAY(res, 1);
while ((line = *(list++)) != NULL)
handle_attr_line(res, line, "[builtin]", ++lineno, 1);
handle_attr_line(res, line, "[builtin]", ++lineno,
READ_ATTR_MACRO_OK);
return res;
}

Expand All @@ -698,29 +703,39 @@ void git_attr_set_direction(enum git_attr_direction new_direction)
direction = new_direction;
}

static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
static struct attr_stack *read_attr_from_file(const char *path, unsigned flags)
{
FILE *fp = fopen_or_warn(path, "r");
int fd;
FILE *fp;
struct attr_stack *res;
char buf[2048];
int lineno = 0;

if (!fp)
if (flags & READ_ATTR_NOFOLLOW)
fd = open_nofollow(path, O_RDONLY);
else
fd = open(path, O_RDONLY);

if (fd < 0) {
warn_on_fopen_errors(path);
return NULL;
}
fp = xfdopen(fd, "r");

CALLOC_ARRAY(res, 1);
while (fgets(buf, sizeof(buf), fp)) {
char *bufp = buf;
if (!lineno)
skip_utf8_bom(&bufp, strlen(bufp));
handle_attr_line(res, bufp, path, ++lineno, macro_ok);
handle_attr_line(res, bufp, path, ++lineno, flags);
}
fclose(fp);
return res;
}

static struct attr_stack *read_attr_from_index(const struct index_state *istate,
const char *path,
int macro_ok)
unsigned flags)
{
struct attr_stack *res;
char *buf, *sp;
Expand All @@ -741,35 +756,35 @@ static struct attr_stack *read_attr_from_index(const struct index_state *istate,
ep = strchrnul(sp, '\n');
more = (*ep == '\n');
*ep = '\0';
handle_attr_line(res, sp, path, ++lineno, macro_ok);
handle_attr_line(res, sp, path, ++lineno, flags);
sp = ep + more;
}
free(buf);
return res;
}

static struct attr_stack *read_attr(const struct index_state *istate,
const char *path, int macro_ok)
const char *path, unsigned flags)
{
struct attr_stack *res = NULL;

if (direction == GIT_ATTR_INDEX) {
res = read_attr_from_index(istate, path, macro_ok);
res = read_attr_from_index(istate, path, flags);
} else if (!is_bare_repository()) {
if (direction == GIT_ATTR_CHECKOUT) {
res = read_attr_from_index(istate, path, macro_ok);
res = read_attr_from_index(istate, path, flags);
if (!res)
res = read_attr_from_file(path, macro_ok);
res = read_attr_from_file(path, flags);
} else if (direction == GIT_ATTR_CHECKIN) {
res = read_attr_from_file(path, macro_ok);
res = read_attr_from_file(path, flags);
if (!res)
/*
* There is no checked out .gitattributes file
* there, but we might have it in the index.
* We allow operation in a sparsely checked out
* work tree, so read from it.
*/
res = read_attr_from_index(istate, path, macro_ok);
res = read_attr_from_index(istate, path, flags);
}
}

Expand Down Expand Up @@ -844,6 +859,7 @@ static void bootstrap_attr_stack(const struct index_state *istate,
struct attr_stack **stack)
{
struct attr_stack *e;
unsigned flags = READ_ATTR_MACRO_OK;

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

/* system-wide frame */
if (git_attr_system()) {
e = read_attr_from_file(git_etc_gitattributes(), 1);
e = read_attr_from_file(git_etc_gitattributes(), flags);
push_stack(stack, e, NULL, 0);
}

/* home directory */
if (get_home_gitattributes()) {
e = read_attr_from_file(get_home_gitattributes(), 1);
e = read_attr_from_file(get_home_gitattributes(), flags);
push_stack(stack, e, NULL, 0);
}

/* root directory */
e = read_attr(istate, GITATTRIBUTES_FILE, 1);
e = read_attr(istate, GITATTRIBUTES_FILE, flags | READ_ATTR_NOFOLLOW);
push_stack(stack, e, xstrdup(""), 0);

/* info frame */
if (startup_info->have_repository)
e = read_attr_from_file(git_path_info_attributes(), 1);
e = read_attr_from_file(git_path_info_attributes(), flags);
else
e = NULL;
if (!e)
Expand Down Expand Up @@ -956,7 +972,7 @@ static void prepare_attr_stack(const struct index_state *istate,
strbuf_add(&pathbuf, path + pathbuf.len, (len - pathbuf.len));
strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE);

next = read_attr(istate, pathbuf.buf, 0);
next = read_attr(istate, pathbuf.buf, READ_ATTR_NOFOLLOW);

/* reset the pathbuf to not include "/.gitattributes" */
strbuf_setlen(&pathbuf, len);
Expand Down
8 changes: 4 additions & 4 deletions builtin/sparse-checkout.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ static int sparse_checkout_list(int argc, const char **argv)
pl.use_cone_patterns = core_sparse_checkout_cone;

sparse_filename = get_sparse_checkout_filename();
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL, 0);
free(sparse_filename);

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

sparse_filename = get_sparse_checkout_filename();
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL);
res = add_patterns_from_file_to_list(sparse_filename, "", 0, &pl, NULL, 0);

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

if (add_patterns_from_file_to_list(sparse_filename, "", 0,
&existing, NULL))
&existing, NULL, 0))
die(_("unable to load existing sparse-checkout patterns"));
free(sparse_filename);

Expand All @@ -507,7 +507,7 @@ static void add_patterns_literal(int argc, const char **argv,
{
char *sparse_filename = get_sparse_checkout_filename();
if (add_patterns_from_file_to_list(sparse_filename, "", 0,
pl, NULL))
pl, NULL, 0))
die(_("unable to load existing sparse-checkout patterns"));
free(sparse_filename);
add_patterns_from_input(pl, argc, argv);
Expand Down
21 changes: 15 additions & 6 deletions dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,9 @@ static int add_patterns_from_buffer(char *buf, size_t size,
const char *base, int baselen,
struct pattern_list *pl);

/* Flags for add_patterns() */
#define PATTERN_NOFOLLOW (1<<0)

/*
* Given a file with name "fname", read it (either from disk, or from
* an index if 'istate' is non-null), parse it and store the
Expand All @@ -1046,15 +1049,19 @@ static int add_patterns_from_buffer(char *buf, size_t size,
*/
static int add_patterns(const char *fname, const char *base, int baselen,
struct pattern_list *pl, struct index_state *istate,
struct oid_stat *oid_stat)
unsigned flags, struct oid_stat *oid_stat)
{
struct stat st;
int r;
int fd;
size_t size = 0;
char *buf;

fd = open(fname, O_RDONLY);
if (flags & PATTERN_NOFOLLOW)
fd = open_nofollow(fname, O_RDONLY);
else
fd = open(fname, O_RDONLY);

if (fd < 0 || fstat(fd, &st) < 0) {
if (fd < 0)
warn_on_fopen_errors(fname);
Expand Down Expand Up @@ -1143,9 +1150,10 @@ static int add_patterns_from_buffer(char *buf, size_t size,

int add_patterns_from_file_to_list(const char *fname, const char *base,
int baselen, struct pattern_list *pl,
struct index_state *istate)
struct index_state *istate,
unsigned flags)
{
return add_patterns(fname, base, baselen, pl, istate, NULL);
return add_patterns(fname, base, baselen, pl, istate, flags, NULL);
}

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

Expand Down Expand Up @@ -1558,6 +1566,7 @@ static void prep_exclude(struct dir_struct *dir,
strbuf_addstr(&sb, dir->exclude_per_dir);
pl->src = strbuf_detach(&sb, NULL);
add_patterns(pl->src, pl->src, stk->baselen, pl, istate,
PATTERN_NOFOLLOW,
untracked ? &oid_stat : NULL);
}
/*
Expand Down Expand Up @@ -3006,7 +3015,7 @@ int get_sparse_checkout_patterns(struct pattern_list *pl)
char *sparse_filename = get_sparse_checkout_filename();

pl->use_cone_patterns = core_sparse_checkout_cone;
res = add_patterns_from_file_to_list(sparse_filename, "", 0, pl, NULL);
res = add_patterns_from_file_to_list(sparse_filename, "", 0, pl, NULL, 0);

free(sparse_filename);
return res;
Expand Down
3 changes: 2 additions & 1 deletion dir.h
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,8 @@ int hashmap_contains_parent(struct hashmap *map,
struct pattern_list *add_pattern_list(struct dir_struct *dir,
int group_type, const char *src);
int add_patterns_from_file_to_list(const char *fname, const char *base, int baselen,
struct pattern_list *pl, struct index_state *istate);
struct pattern_list *pl, struct index_state *istate,
unsigned flags);
void add_patterns_from_file(struct dir_struct *, const char *fname);
int add_patterns_from_blob_to_list(struct object_id *oid,
const char *base, int baselen,
Expand Down
7 changes: 7 additions & 0 deletions git-compat-util.h
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,13 @@ int access_or_die(const char *path, int mode, unsigned flag);
/* Warn on an inaccessible file if errno indicates this is an error */
int warn_on_fopen_errors(const char *path);

/*
* Open with O_NOFOLLOW, or equivalent. Note that the fallback equivalent
* may be racy. Do not use this as protection against an attacker who can
* simultaneously create paths.
*/
int open_nofollow(const char *path, int flags);

#if !defined(USE_PARENS_AROUND_GETTEXT_N) && defined(__GNUC__)
#define USE_PARENS_AROUND_GETTEXT_N 1
#endif
Expand Down
22 changes: 17 additions & 5 deletions mailmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,20 +157,30 @@ static void read_mailmap_line(struct string_list *map, char *buffer)
add_mapping(map, name1, email1, name2, email2);
}

static int read_mailmap_file(struct string_list *map, const char *filename)
/* Flags for read_mailmap_file() */
#define MAILMAP_NOFOLLOW (1<<0)

static int read_mailmap_file(struct string_list *map, const char *filename,
unsigned flags)
{
char buffer[1024];
FILE *f;
int fd;

if (!filename)
return 0;

f = fopen(filename, "r");
if (!f) {
if (flags & MAILMAP_NOFOLLOW)
fd = open_nofollow(filename, O_RDONLY);
else
fd = open(filename, O_RDONLY);

if (fd < 0) {
if (errno == ENOENT)
return 0;
return error_errno("unable to open mailmap at %s", filename);
}
f = xfdopen(fd, "r");

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

if (!startup_info->have_repository || !is_bare_repository())
err |= read_mailmap_file(map, ".mailmap");
err |= read_mailmap_file(map, ".mailmap",
startup_info->have_repository ?
MAILMAP_NOFOLLOW : 0);
if (startup_info->have_repository)
err |= read_mailmap_blob(map, git_mailmap_blob);
err |= read_mailmap_file(map, git_mailmap_file);
err |= read_mailmap_file(map, git_mailmap_file, 0);
return err;
}

Expand Down

0 comments on commit 204333b

Please sign in to comment.