revert(3p/git): Revert merge of git upstream at v2.26.2

This causes cgit to serve error pages, which is undesirable.

This reverts commit 5229c9b232, reversing
changes made to f2b211131f.
This commit is contained in:
Vincent Ambo 2020-05-26 00:06:52 +01:00
parent 6f8fbf4aa4
commit 93ba78d6f4
1006 changed files with 60537 additions and 148724 deletions

548
third_party/git/fsck.c vendored
View file

@ -9,14 +9,12 @@
#include "tag.h"
#include "fsck.h"
#include "refs.h"
#include "url.h"
#include "utf8.h"
#include "decorate.h"
#include "oidset.h"
#include "packfile.h"
#include "submodule-config.h"
#include "config.h"
#include "credential.h"
#include "help.h"
static struct oidset gitmodules_found = OIDSET_INIT;
@ -45,17 +43,21 @@ static struct oidset gitmodules_done = OIDSET_INIT;
FUNC(MISSING_AUTHOR, ERROR) \
FUNC(MISSING_COMMITTER, ERROR) \
FUNC(MISSING_EMAIL, ERROR) \
FUNC(MISSING_GRAFT, ERROR) \
FUNC(MISSING_NAME_BEFORE_EMAIL, ERROR) \
FUNC(MISSING_OBJECT, ERROR) \
FUNC(MISSING_PARENT, ERROR) \
FUNC(MISSING_SPACE_BEFORE_DATE, ERROR) \
FUNC(MISSING_SPACE_BEFORE_EMAIL, ERROR) \
FUNC(MISSING_TAG, ERROR) \
FUNC(MISSING_TAG_ENTRY, ERROR) \
FUNC(MISSING_TAG_OBJECT, ERROR) \
FUNC(MISSING_TREE, ERROR) \
FUNC(MISSING_TREE_OBJECT, ERROR) \
FUNC(MISSING_TYPE, ERROR) \
FUNC(MISSING_TYPE_ENTRY, ERROR) \
FUNC(MULTIPLE_AUTHORS, ERROR) \
FUNC(TAG_OBJECT_NOT_TAG, ERROR) \
FUNC(TREE_NOT_SORTED, ERROR) \
FUNC(UNKNOWN_TYPE, ERROR) \
FUNC(ZERO_PADDED_DATE, ERROR) \
@ -66,7 +68,6 @@ static struct oidset gitmodules_done = OIDSET_INIT;
FUNC(GITMODULES_SYMLINK, ERROR) \
FUNC(GITMODULES_URL, ERROR) \
FUNC(GITMODULES_PATH, ERROR) \
FUNC(GITMODULES_UPDATE, ERROR) \
/* warnings */ \
FUNC(BAD_FILEMODE, WARN) \
FUNC(EMPTY_NAME, WARN) \
@ -280,16 +281,14 @@ static void append_msg_id(struct strbuf *sb, const char *msg_id)
strbuf_addstr(sb, ": ");
}
static int object_on_skiplist(struct fsck_options *opts,
const struct object_id *oid)
static int object_on_skiplist(struct fsck_options *opts, struct object *obj)
{
return opts && oid && oidset_contains(&opts->skiplist, oid);
return opts && obj && oidset_contains(&opts->skiplist, &obj->oid);
}
__attribute__((format (printf, 5, 6)))
static int report(struct fsck_options *options,
const struct object_id *oid, enum object_type object_type,
enum fsck_msg_id id, const char *fmt, ...)
__attribute__((format (printf, 4, 5)))
static int report(struct fsck_options *options, struct object *object,
enum fsck_msg_id id, const char *fmt, ...)
{
va_list ap;
struct strbuf sb = STRBUF_INIT;
@ -298,7 +297,7 @@ static int report(struct fsck_options *options,
if (msg_type == FSCK_IGNORE)
return 0;
if (object_on_skiplist(options, oid))
if (object_on_skiplist(options, object))
return 0;
if (msg_type == FSCK_FATAL)
@ -310,71 +309,49 @@ static int report(struct fsck_options *options,
va_start(ap, fmt);
strbuf_vaddf(&sb, fmt, ap);
result = options->error_func(options, oid, object_type,
msg_type, sb.buf);
result = options->error_func(options, object, msg_type, sb.buf);
strbuf_release(&sb);
va_end(ap);
return result;
}
void fsck_enable_object_names(struct fsck_options *options)
static char *get_object_name(struct fsck_options *options, struct object *obj)
{
if (!options->object_names)
options->object_names = kh_init_oid_map();
return NULL;
return lookup_decoration(options->object_names, obj);
}
const char *fsck_get_object_name(struct fsck_options *options,
const struct object_id *oid)
{
khiter_t pos;
if (!options->object_names)
return NULL;
pos = kh_get_oid_map(options->object_names, *oid);
if (pos >= kh_end(options->object_names))
return NULL;
return kh_value(options->object_names, pos);
}
void fsck_put_object_name(struct fsck_options *options,
const struct object_id *oid,
const char *fmt, ...)
static void put_object_name(struct fsck_options *options, struct object *obj,
const char *fmt, ...)
{
va_list ap;
struct strbuf buf = STRBUF_INIT;
khiter_t pos;
int hashret;
char *existing;
if (!options->object_names)
return;
pos = kh_put_oid_map(options->object_names, *oid, &hashret);
if (!hashret)
existing = lookup_decoration(options->object_names, obj);
if (existing)
return;
va_start(ap, fmt);
strbuf_vaddf(&buf, fmt, ap);
kh_value(options->object_names, pos) = strbuf_detach(&buf, NULL);
add_decoration(options->object_names, obj, strbuf_detach(&buf, NULL));
va_end(ap);
}
const char *fsck_describe_object(struct fsck_options *options,
const struct object_id *oid)
static const char *describe_object(struct fsck_options *o, struct object *obj)
{
static struct strbuf bufs[] = {
STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
};
static int b = 0;
struct strbuf *buf;
const char *name = fsck_get_object_name(options, oid);
static struct strbuf buf = STRBUF_INIT;
char *name;
buf = bufs + b;
b = (b + 1) % ARRAY_SIZE(bufs);
strbuf_reset(buf);
strbuf_addstr(buf, oid_to_hex(oid));
if (name)
strbuf_addf(buf, " (%s)", name);
strbuf_reset(&buf);
strbuf_addstr(&buf, oid_to_hex(&obj->oid));
if (o->object_names && (name = lookup_decoration(o->object_names, obj)))
strbuf_addf(&buf, " (%s)", name);
return buf->buf;
return buf.buf;
}
static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *options)
@ -387,7 +364,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
if (parse_tree(tree))
return -1;
name = fsck_get_object_name(options, &tree->object.oid);
name = get_object_name(options, &tree->object);
if (init_tree_desc_gently(&desc, tree->buffer, tree->size))
return -1;
while (tree_entry_gently(&desc, &entry)) {
@ -400,21 +377,20 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
if (S_ISDIR(entry.mode)) {
obj = (struct object *)lookup_tree(the_repository, &entry.oid);
if (name && obj)
fsck_put_object_name(options, &entry.oid, "%s%s/",
name, entry.path);
put_object_name(options, obj, "%s%s/", name,
entry.path);
result = options->walk(obj, OBJ_TREE, data, options);
}
else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {
obj = (struct object *)lookup_blob(the_repository, &entry.oid);
if (name && obj)
fsck_put_object_name(options, &entry.oid, "%s%s",
name, entry.path);
put_object_name(options, obj, "%s%s", name,
entry.path);
result = options->walk(obj, OBJ_BLOB, data, options);
}
else {
result = error("in tree %s: entry %s has bad mode %.6o",
fsck_describe_object(options, &tree->object.oid),
entry.path, entry.mode);
describe_object(options, &tree->object), entry.path, entry.mode);
}
if (result < 0)
return result;
@ -435,10 +411,10 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
if (parse_commit(commit))
return -1;
name = fsck_get_object_name(options, &commit->object.oid);
name = get_object_name(options, &commit->object);
if (name)
fsck_put_object_name(options, get_commit_tree_oid(commit),
"%s:", name);
put_object_name(options, &get_commit_tree(commit)->object,
"%s:", name);
result = options->walk((struct object *)get_commit_tree(commit),
OBJ_TREE, data, options);
@ -466,17 +442,16 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
while (parents) {
if (name) {
struct object_id *oid = &parents->item->object.oid;
struct object *obj = &parents->item->object;
if (counter++)
fsck_put_object_name(options, oid, "%s^%d",
name, counter);
put_object_name(options, obj, "%s^%d",
name, counter);
else if (generation > 0)
fsck_put_object_name(options, oid, "%.*s~%d",
name_prefix_len, name,
generation + 1);
put_object_name(options, obj, "%.*s~%d",
name_prefix_len, name, generation + 1);
else
fsck_put_object_name(options, oid, "%s^", name);
put_object_name(options, obj, "%s^", name);
}
result = options->walk((struct object *)parents->item, OBJ_COMMIT, data, options);
if (result < 0)
@ -490,12 +465,12 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
static int fsck_walk_tag(struct tag *tag, void *data, struct fsck_options *options)
{
const char *name = fsck_get_object_name(options, &tag->object.oid);
char *name = get_object_name(options, &tag->object);
if (parse_tag(tag))
return -1;
if (name)
fsck_put_object_name(options, &tag->tagged->oid, "%s", name);
put_object_name(options, tag->tagged, "%s", name);
return options->walk(tag->tagged, OBJ_ANY, data, options);
}
@ -517,8 +492,7 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options)
case OBJ_TAG:
return fsck_walk_tag((struct tag *)obj, data, options);
default:
error("Unknown object type for %s",
fsck_describe_object(options, &obj->oid));
error("Unknown object type for %s", describe_object(options, obj));
return -1;
}
}
@ -569,9 +543,7 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con
return c1 < c2 ? 0 : TREE_UNORDERED;
}
static int fsck_tree(const struct object_id *oid,
const char *buffer, unsigned long size,
struct fsck_options *options)
static int fsck_tree(struct tree *item, struct fsck_options *options)
{
int retval = 0;
int has_null_sha1 = 0;
@ -588,8 +560,8 @@ static int fsck_tree(const struct object_id *oid,
unsigned o_mode;
const char *o_name;
if (init_tree_desc_gently(&desc, buffer, size)) {
retval += report(options, oid, OBJ_TREE, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
if (init_tree_desc_gently(&desc, item->buffer, item->size)) {
retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
return retval;
}
@ -598,7 +570,7 @@ static int fsck_tree(const struct object_id *oid,
while (desc.size) {
unsigned short mode;
const char *name, *backslash;
const char *name;
const struct object_id *oid;
oid = tree_entry_extract(&desc, &name, &mode);
@ -615,30 +587,13 @@ static int fsck_tree(const struct object_id *oid,
if (!S_ISLNK(mode))
oidset_insert(&gitmodules_found, oid);
else
retval += report(options,
oid, OBJ_TREE,
retval += report(options, &item->object,
FSCK_MSG_GITMODULES_SYMLINK,
".gitmodules is a symbolic link");
}
if ((backslash = strchr(name, '\\'))) {
while (backslash) {
backslash++;
has_dotgit |= is_ntfs_dotgit(backslash);
if (is_ntfs_dotgitmodules(backslash)) {
if (!S_ISLNK(mode))
oidset_insert(&gitmodules_found, oid);
else
retval += report(options, oid, OBJ_TREE,
FSCK_MSG_GITMODULES_SYMLINK,
".gitmodules is a symbolic link");
}
backslash = strchr(backslash, '\\');
}
}
if (update_tree_entry_gently(&desc)) {
retval += report(options, oid, OBJ_TREE, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
break;
}
@ -683,31 +638,30 @@ static int fsck_tree(const struct object_id *oid,
}
if (has_null_sha1)
retval += report(options, oid, OBJ_TREE, FSCK_MSG_NULL_SHA1, "contains entries pointing to null sha1");
retval += report(options, &item->object, FSCK_MSG_NULL_SHA1, "contains entries pointing to null sha1");
if (has_full_path)
retval += report(options, oid, OBJ_TREE, FSCK_MSG_FULL_PATHNAME, "contains full pathnames");
retval += report(options, &item->object, FSCK_MSG_FULL_PATHNAME, "contains full pathnames");
if (has_empty_name)
retval += report(options, oid, OBJ_TREE, FSCK_MSG_EMPTY_NAME, "contains empty pathname");
retval += report(options, &item->object, FSCK_MSG_EMPTY_NAME, "contains empty pathname");
if (has_dot)
retval += report(options, oid, OBJ_TREE, FSCK_MSG_HAS_DOT, "contains '.'");
retval += report(options, &item->object, FSCK_MSG_HAS_DOT, "contains '.'");
if (has_dotdot)
retval += report(options, oid, OBJ_TREE, FSCK_MSG_HAS_DOTDOT, "contains '..'");
retval += report(options, &item->object, FSCK_MSG_HAS_DOTDOT, "contains '..'");
if (has_dotgit)
retval += report(options, oid, OBJ_TREE, FSCK_MSG_HAS_DOTGIT, "contains '.git'");
retval += report(options, &item->object, FSCK_MSG_HAS_DOTGIT, "contains '.git'");
if (has_zero_pad)
retval += report(options, oid, OBJ_TREE, FSCK_MSG_ZERO_PADDED_FILEMODE, "contains zero-padded file modes");
retval += report(options, &item->object, FSCK_MSG_ZERO_PADDED_FILEMODE, "contains zero-padded file modes");
if (has_bad_modes)
retval += report(options, oid, OBJ_TREE, FSCK_MSG_BAD_FILEMODE, "contains bad file modes");
retval += report(options, &item->object, FSCK_MSG_BAD_FILEMODE, "contains bad file modes");
if (has_dup_entries)
retval += report(options, oid, OBJ_TREE, FSCK_MSG_DUPLICATE_ENTRIES, "contains duplicate file entries");
retval += report(options, &item->object, FSCK_MSG_DUPLICATE_ENTRIES, "contains duplicate file entries");
if (not_properly_sorted)
retval += report(options, oid, OBJ_TREE, FSCK_MSG_TREE_NOT_SORTED, "not properly sorted");
retval += report(options, &item->object, FSCK_MSG_TREE_NOT_SORTED, "not properly sorted");
return retval;
}
static int verify_headers(const void *data, unsigned long size,
const struct object_id *oid, enum object_type type,
struct fsck_options *options)
struct object *obj, struct fsck_options *options)
{
const char *buffer = (const char *)data;
unsigned long i;
@ -715,7 +669,7 @@ static int verify_headers(const void *data, unsigned long size,
for (i = 0; i < size; i++) {
switch (buffer[i]) {
case '\0':
return report(options, oid, type,
return report(options, obj,
FSCK_MSG_NUL_IN_HEADER,
"unterminated header: NUL at offset %ld", i);
case '\n':
@ -733,13 +687,11 @@ static int verify_headers(const void *data, unsigned long size,
if (size && buffer[size - 1] == '\n')
return 0;
return report(options, oid, type,
return report(options, obj,
FSCK_MSG_UNTERMINATED_HEADER, "unterminated header");
}
static int fsck_ident(const char **ident,
const struct object_id *oid, enum object_type type,
struct fsck_options *options)
static int fsck_ident(const char **ident, struct object *obj, struct fsck_options *options)
{
const char *p = *ident;
char *end;
@ -749,28 +701,28 @@ static int fsck_ident(const char **ident,
(*ident)++;
if (*p == '<')
return report(options, oid, type, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, "invalid author/committer line - missing space before email");
return report(options, obj, FSCK_MSG_MISSING_NAME_BEFORE_EMAIL, "invalid author/committer line - missing space before email");
p += strcspn(p, "<>\n");
if (*p == '>')
return report(options, oid, type, FSCK_MSG_BAD_NAME, "invalid author/committer line - bad name");
return report(options, obj, FSCK_MSG_BAD_NAME, "invalid author/committer line - bad name");
if (*p != '<')
return report(options, oid, type, FSCK_MSG_MISSING_EMAIL, "invalid author/committer line - missing email");
return report(options, obj, FSCK_MSG_MISSING_EMAIL, "invalid author/committer line - missing email");
if (p[-1] != ' ')
return report(options, oid, type, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, "invalid author/committer line - missing space before email");
return report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_EMAIL, "invalid author/committer line - missing space before email");
p++;
p += strcspn(p, "<>\n");
if (*p != '>')
return report(options, oid, type, FSCK_MSG_BAD_EMAIL, "invalid author/committer line - bad email");
return report(options, obj, FSCK_MSG_BAD_EMAIL, "invalid author/committer line - bad email");
p++;
if (*p != ' ')
return report(options, oid, type, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, "invalid author/committer line - missing space before date");
return report(options, obj, FSCK_MSG_MISSING_SPACE_BEFORE_DATE, "invalid author/committer line - missing space before date");
p++;
if (*p == '0' && p[1] != ' ')
return report(options, oid, type, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date");
return report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date");
if (date_overflows(parse_timestamp(p, &end, 10)))
return report(options, oid, type, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow");
return report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow");
if ((end == p || *end != ' '))
return report(options, oid, type, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date");
return report(options, obj, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date");
p = end + 1;
if ((*p != '+' && *p != '-') ||
!isdigit(p[1]) ||
@ -778,60 +730,83 @@ static int fsck_ident(const char **ident,
!isdigit(p[3]) ||
!isdigit(p[4]) ||
(p[5] != '\n'))
return report(options, oid, type, FSCK_MSG_BAD_TIMEZONE, "invalid author/committer line - bad time zone");
return report(options, obj, FSCK_MSG_BAD_TIMEZONE, "invalid author/committer line - bad time zone");
p += 6;
return 0;
}
static int fsck_commit(const struct object_id *oid,
const char *buffer, unsigned long size,
struct fsck_options *options)
static int fsck_commit_buffer(struct commit *commit, const char *buffer,
unsigned long size, struct fsck_options *options)
{
struct object_id tree_oid, parent_oid;
unsigned author_count;
struct object_id tree_oid, oid;
struct commit_graft *graft;
unsigned parent_count, parent_line_count = 0, author_count;
int err;
const char *buffer_begin = buffer;
const char *p;
if (verify_headers(buffer, size, oid, OBJ_COMMIT, options))
if (verify_headers(buffer, size, &commit->object, options))
return -1;
if (!skip_prefix(buffer, "tree ", &buffer))
return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line");
return report(options, &commit->object, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line");
if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') {
err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
err = report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
if (err)
return err;
}
buffer = p + 1;
while (skip_prefix(buffer, "parent ", &buffer)) {
if (parse_oid_hex(buffer, &parent_oid, &p) || *p != '\n') {
err = report(options, oid, OBJ_COMMIT, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1");
if (parse_oid_hex(buffer, &oid, &p) || *p != '\n') {
err = report(options, &commit->object, FSCK_MSG_BAD_PARENT_SHA1, "invalid 'parent' line format - bad sha1");
if (err)
return err;
}
buffer = p + 1;
parent_line_count++;
}
graft = lookup_commit_graft(the_repository, &commit->object.oid);
parent_count = commit_list_count(commit->parents);
if (graft) {
if (graft->nr_parent == -1 && !parent_count)
; /* shallow commit */
else if (graft->nr_parent != parent_count) {
err = report(options, &commit->object, FSCK_MSG_MISSING_GRAFT, "graft objects missing");
if (err)
return err;
}
} else {
if (parent_count != parent_line_count) {
err = report(options, &commit->object, FSCK_MSG_MISSING_PARENT, "parent objects missing");
if (err)
return err;
}
}
author_count = 0;
while (skip_prefix(buffer, "author ", &buffer)) {
author_count++;
err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
err = fsck_ident(&buffer, &commit->object, options);
if (err)
return err;
}
if (author_count < 1)
err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line");
err = report(options, &commit->object, FSCK_MSG_MISSING_AUTHOR, "invalid format - expected 'author' line");
else if (author_count > 1)
err = report(options, oid, OBJ_COMMIT, FSCK_MSG_MULTIPLE_AUTHORS, "invalid format - multiple 'author' lines");
err = report(options, &commit->object, FSCK_MSG_MULTIPLE_AUTHORS, "invalid format - multiple 'author' lines");
if (err)
return err;
if (!skip_prefix(buffer, "committer ", &buffer))
return report(options, oid, OBJ_COMMIT, FSCK_MSG_MISSING_COMMITTER, "invalid format - expected 'committer' line");
err = fsck_ident(&buffer, oid, OBJ_COMMIT, options);
return report(options, &commit->object, FSCK_MSG_MISSING_COMMITTER, "invalid format - expected 'committer' line");
err = fsck_ident(&buffer, &commit->object, options);
if (err)
return err;
if (!get_commit_tree(commit)) {
err = report(options, &commit->object, FSCK_MSG_BAD_TREE, "could not load commit's tree %s", oid_to_hex(&tree_oid));
if (err)
return err;
}
if (memchr(buffer_begin, '\0', size)) {
err = report(options, oid, OBJ_COMMIT, FSCK_MSG_NUL_IN_COMMIT,
err = report(options, &commit->object, FSCK_MSG_NUL_IN_COMMIT,
"NUL byte in the commit object body");
if (err)
return err;
@ -839,60 +814,91 @@ static int fsck_commit(const struct object_id *oid,
return 0;
}
static int fsck_tag(const struct object_id *oid, const char *buffer,
unsigned long size, struct fsck_options *options)
static int fsck_commit(struct commit *commit, const char *data,
unsigned long size, struct fsck_options *options)
{
struct object_id tagged_oid;
const char *buffer = data ? data : get_commit_buffer(commit, &size);
int ret = fsck_commit_buffer(commit, buffer, size, options);
if (!data)
unuse_commit_buffer(commit, buffer);
return ret;
}
static int fsck_tag_buffer(struct tag *tag, const char *data,
unsigned long size, struct fsck_options *options)
{
struct object_id oid;
int ret = 0;
char *eol;
const char *buffer;
char *to_free = NULL, *eol;
struct strbuf sb = STRBUF_INIT;
const char *p;
ret = verify_headers(buffer, size, oid, OBJ_TAG, options);
if (data)
buffer = data;
else {
enum object_type type;
buffer = to_free =
read_object_file(&tag->object.oid, &type, &size);
if (!buffer)
return report(options, &tag->object,
FSCK_MSG_MISSING_TAG_OBJECT,
"cannot read tag object");
if (type != OBJ_TAG) {
ret = report(options, &tag->object,
FSCK_MSG_TAG_OBJECT_NOT_TAG,
"expected tag got %s",
type_name(type));
goto done;
}
}
ret = verify_headers(buffer, size, &tag->object, options);
if (ret)
goto done;
if (!skip_prefix(buffer, "object ", &buffer)) {
ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line");
ret = report(options, &tag->object, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line");
goto done;
}
if (parse_oid_hex(buffer, &tagged_oid, &p) || *p != '\n') {
ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1");
if (parse_oid_hex(buffer, &oid, &p) || *p != '\n') {
ret = report(options, &tag->object, FSCK_MSG_BAD_OBJECT_SHA1, "invalid 'object' line format - bad sha1");
if (ret)
goto done;
}
buffer = p + 1;
if (!skip_prefix(buffer, "type ", &buffer)) {
ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE_ENTRY, "invalid format - expected 'type' line");
ret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE_ENTRY, "invalid format - expected 'type' line");
goto done;
}
eol = strchr(buffer, '\n');
if (!eol) {
ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TYPE, "invalid format - unexpected end after 'type' line");
ret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE, "invalid format - unexpected end after 'type' line");
goto done;
}
if (type_from_string_gently(buffer, eol - buffer, 1) < 0)
ret = report(options, oid, OBJ_TAG, FSCK_MSG_BAD_TYPE, "invalid 'type' value");
ret = report(options, &tag->object, FSCK_MSG_BAD_TYPE, "invalid 'type' value");
if (ret)
goto done;
buffer = eol + 1;
if (!skip_prefix(buffer, "tag ", &buffer)) {
ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAG_ENTRY, "invalid format - expected 'tag' line");
ret = report(options, &tag->object, FSCK_MSG_MISSING_TAG_ENTRY, "invalid format - expected 'tag' line");
goto done;
}
eol = strchr(buffer, '\n');
if (!eol) {
ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAG, "invalid format - unexpected end after 'type' line");
ret = report(options, &tag->object, FSCK_MSG_MISSING_TAG, "invalid format - unexpected end after 'type' line");
goto done;
}
strbuf_addf(&sb, "refs/tags/%.*s", (int)(eol - buffer), buffer);
if (check_refname_format(sb.buf, 0)) {
ret = report(options, oid, OBJ_TAG,
FSCK_MSG_BAD_TAG_NAME,
"invalid 'tag' name: %.*s",
(int)(eol - buffer), buffer);
ret = report(options, &tag->object, FSCK_MSG_BAD_TAG_NAME,
"invalid 'tag' name: %.*s",
(int)(eol - buffer), buffer);
if (ret)
goto done;
}
@ -900,163 +906,32 @@ static int fsck_tag(const struct object_id *oid, const char *buffer,
if (!skip_prefix(buffer, "tagger ", &buffer)) {
/* early tags do not contain 'tagger' lines; warn only */
ret = report(options, oid, OBJ_TAG, FSCK_MSG_MISSING_TAGGER_ENTRY, "invalid format - expected 'tagger' line");
ret = report(options, &tag->object, FSCK_MSG_MISSING_TAGGER_ENTRY, "invalid format - expected 'tagger' line");
if (ret)
goto done;
}
else
ret = fsck_ident(&buffer, oid, OBJ_TAG, options);
ret = fsck_ident(&buffer, &tag->object, options);
done:
strbuf_release(&sb);
free(to_free);
return ret;
}
/*
* Like builtin/submodule--helper.c's starts_with_dot_slash, but without
* relying on the platform-dependent is_dir_sep helper.
*
* This is for use in checking whether a submodule URL is interpreted as
* relative to the current directory on any platform, since \ is a
* directory separator on Windows but not on other platforms.
*/
static int starts_with_dot_slash(const char *str)
static int fsck_tag(struct tag *tag, const char *data,
unsigned long size, struct fsck_options *options)
{
return str[0] == '.' && (str[1] == '/' || str[1] == '\\');
}
struct object *tagged = tag->tagged;
/*
* Like starts_with_dot_slash, this is a variant of submodule--helper's
* helper of the same name with the twist that it accepts backslash as a
* directory separator even on non-Windows platforms.
*/
static int starts_with_dot_dot_slash(const char *str)
{
return str[0] == '.' && starts_with_dot_slash(str + 1);
}
if (!tagged)
return report(options, &tag->object, FSCK_MSG_BAD_TAG_OBJECT, "could not load tagged object");
static int submodule_url_is_relative(const char *url)
{
return starts_with_dot_slash(url) || starts_with_dot_dot_slash(url);
}
/*
* Count directory components that a relative submodule URL should chop
* from the remote_url it is to be resolved against.
*
* In other words, this counts "../" components at the start of a
* submodule URL.
*
* Returns the number of directory components to chop and writes a
* pointer to the next character of url after all leading "./" and
* "../" components to out.
*/
static int count_leading_dotdots(const char *url, const char **out)
{
int result = 0;
while (1) {
if (starts_with_dot_dot_slash(url)) {
result++;
url += strlen("../");
continue;
}
if (starts_with_dot_slash(url)) {
url += strlen("./");
continue;
}
*out = url;
return result;
}
}
/*
* Check whether a transport is implemented by git-remote-curl.
*
* If it is, returns 1 and writes the URL that would be passed to
* git-remote-curl to the "out" parameter.
*
* Otherwise, returns 0 and leaves "out" untouched.
*
* Examples:
* http::https://example.com/repo.git -> 1, https://example.com/repo.git
* https://example.com/repo.git -> 1, https://example.com/repo.git
* git://example.com/repo.git -> 0
*
* This is for use in checking for previously exploitable bugs that
* required a submodule URL to be passed to git-remote-curl.
*/
static int url_to_curl_url(const char *url, const char **out)
{
/*
* We don't need to check for case-aliases, "http.exe", and so
* on because in the default configuration, is_transport_allowed
* prevents URLs with those schemes from being cloned
* automatically.
*/
if (skip_prefix(url, "http::", out) ||
skip_prefix(url, "https::", out) ||
skip_prefix(url, "ftp::", out) ||
skip_prefix(url, "ftps::", out))
return 1;
if (starts_with(url, "http://") ||
starts_with(url, "https://") ||
starts_with(url, "ftp://") ||
starts_with(url, "ftps://")) {
*out = url;
return 1;
}
return 0;
}
static int check_submodule_url(const char *url)
{
const char *curl_url;
if (looks_like_command_line_option(url))
return -1;
if (submodule_url_is_relative(url)) {
char *decoded;
const char *next;
int has_nl;
/*
* This could be appended to an http URL and url-decoded;
* check for malicious characters.
*/
decoded = url_decode(url);
has_nl = !!strchr(decoded, '\n');
free(decoded);
if (has_nl)
return -1;
/*
* URLs which escape their root via "../" can overwrite
* the host field and previous components, resolving to
* URLs like https::example.com/submodule.git and
* https:///example.com/submodule.git that were
* susceptible to CVE-2020-11008.
*/
if (count_leading_dotdots(url, &next) > 0 &&
(*next == ':' || *next == '/'))
return -1;
}
else if (url_to_curl_url(url, &curl_url)) {
struct credential c = CREDENTIAL_INIT;
int ret = 0;
if (credential_from_url_gently(&c, curl_url, 1) ||
!*c.host)
ret = -1;
credential_clear(&c);
return ret;
}
return 0;
return fsck_tag_buffer(tag, data, size, options);
}
struct fsck_gitmodules_data {
const struct object_id *oid;
struct object *obj;
struct fsck_options *options;
int ret;
};
@ -1074,47 +949,38 @@ static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
name = xmemdupz(subsection, subsection_len);
if (check_submodule_name(name) < 0)
data->ret |= report(data->options,
data->oid, OBJ_BLOB,
data->ret |= report(data->options, data->obj,
FSCK_MSG_GITMODULES_NAME,
"disallowed submodule name: %s",
name);
if (!strcmp(key, "url") && value &&
check_submodule_url(value) < 0)
data->ret |= report(data->options,
data->oid, OBJ_BLOB,
looks_like_command_line_option(value))
data->ret |= report(data->options, data->obj,
FSCK_MSG_GITMODULES_URL,
"disallowed submodule url: %s",
value);
if (!strcmp(key, "path") && value &&
looks_like_command_line_option(value))
data->ret |= report(data->options,
data->oid, OBJ_BLOB,
data->ret |= report(data->options, data->obj,
FSCK_MSG_GITMODULES_PATH,
"disallowed submodule path: %s",
value);
if (!strcmp(key, "update") && value &&
parse_submodule_update_type(value) == SM_UPDATE_COMMAND)
data->ret |= report(data->options, data->oid, OBJ_BLOB,
FSCK_MSG_GITMODULES_UPDATE,
"disallowed submodule update setting: %s",
value);
free(name);
return 0;
}
static int fsck_blob(const struct object_id *oid, const char *buf,
static int fsck_blob(struct blob *blob, const char *buf,
unsigned long size, struct fsck_options *options)
{
struct fsck_gitmodules_data data;
struct config_options config_opts = { 0 };
if (!oidset_contains(&gitmodules_found, oid))
if (!oidset_contains(&gitmodules_found, &blob->object.oid))
return 0;
oidset_insert(&gitmodules_done, oid);
oidset_insert(&gitmodules_done, &blob->object.oid);
if (object_on_skiplist(options, oid))
if (object_on_skiplist(options, &blob->object))
return 0;
if (!buf) {
@ -1123,18 +989,18 @@ static int fsck_blob(const struct object_id *oid, const char *buf,
* blob too gigantic to load into memory. Let's just consider
* that an error.
*/
return report(options, oid, OBJ_BLOB,
return report(options, &blob->object,
FSCK_MSG_GITMODULES_LARGE,
".gitmodules too large to parse");
}
data.oid = oid;
data.obj = &blob->object;
data.options = options;
data.ret = 0;
config_opts.error_action = CONFIG_ERROR_SILENT;
if (git_config_from_mem(fsck_gitmodules_fn, CONFIG_ORIGIN_BLOB,
".gitmodules", buf, size, &data, &config_opts))
data.ret |= report(options, oid, OBJ_BLOB,
data.ret |= report(options, &blob->object,
FSCK_MSG_GITMODULES_PARSE,
"could not parse gitmodules blob");
@ -1145,33 +1011,31 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
struct fsck_options *options)
{
if (!obj)
return report(options, NULL, OBJ_NONE, FSCK_MSG_BAD_OBJECT_SHA1, "no valid object to fsck");
return report(options, obj, FSCK_MSG_BAD_OBJECT_SHA1, "no valid object to fsck");
if (obj->type == OBJ_BLOB)
return fsck_blob(&obj->oid, data, size, options);
return fsck_blob((struct blob *)obj, data, size, options);
if (obj->type == OBJ_TREE)
return fsck_tree(&obj->oid, data, size, options);
return fsck_tree((struct tree *) obj, options);
if (obj->type == OBJ_COMMIT)
return fsck_commit(&obj->oid, data, size, options);
return fsck_commit((struct commit *) obj, (const char *) data,
size, options);
if (obj->type == OBJ_TAG)
return fsck_tag(&obj->oid, data, size, options);
return fsck_tag((struct tag *) obj, (const char *) data,
size, options);
return report(options, &obj->oid, obj->type,
FSCK_MSG_UNKNOWN_TYPE,
"unknown type '%d' (internal fsck error)",
obj->type);
return report(options, obj, FSCK_MSG_UNKNOWN_TYPE, "unknown type '%d' (internal fsck error)",
obj->type);
}
int fsck_error_function(struct fsck_options *o,
const struct object_id *oid,
enum object_type object_type,
int msg_type, const char *message)
struct object *obj, int msg_type, const char *message)
{
if (msg_type == FSCK_WARN) {
warning("object %s: %s", fsck_describe_object(o, oid), message);
warning("object %s: %s", describe_object(o, obj), message);
return 0;
}
error("object %s: %s", fsck_describe_object(o, oid), message);
error("object %s: %s", describe_object(o, obj), message);
return 1;
}
@ -1183,6 +1047,7 @@ int fsck_finish(struct fsck_options *options)
oidset_iter_init(&gitmodules_found, &iter);
while ((oid = oidset_iter_next(&iter))) {
struct blob *blob;
enum object_type type;
unsigned long size;
char *buf;
@ -1190,22 +1055,29 @@ int fsck_finish(struct fsck_options *options)
if (oidset_contains(&gitmodules_done, oid))
continue;
blob = lookup_blob(the_repository, oid);
if (!blob) {
struct object *obj = lookup_unknown_object(oid);
ret |= report(options, obj,
FSCK_MSG_GITMODULES_BLOB,
"non-blob found at .gitmodules");
continue;
}
buf = read_object_file(oid, &type, &size);
if (!buf) {
if (is_promisor_object(oid))
if (is_promisor_object(&blob->object.oid))
continue;
ret |= report(options,
oid, OBJ_BLOB,
ret |= report(options, &blob->object,
FSCK_MSG_GITMODULES_MISSING,
"unable to read .gitmodules blob");
continue;
}
if (type == OBJ_BLOB)
ret |= fsck_blob(oid, buf, size, options);
ret |= fsck_blob(blob, buf, size, options);
else
ret |= report(options,
oid, type,
ret |= report(options, &blob->object,
FSCK_MSG_GITMODULES_BLOB,
"non-blob found at .gitmodules");
free(buf);