#include "utf8.h"
#include "sha1-array.h"
#include "decorate.h"
+#include "oidset.h"
+#include "packfile.h"
+#include "submodule-config.h"
+#include "config.h"
+#include "help.h"
+
+static struct oidset gitmodules_found = OIDSET_INIT;
+static struct oidset gitmodules_done = OIDSET_INIT;
#define FSCK_FATAL -1
#define FSCK_INFO -2
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(TREE_NOT_SORTED, ERROR) \
FUNC(UNKNOWN_TYPE, ERROR) \
FUNC(ZERO_PADDED_DATE, ERROR) \
+ FUNC(GITMODULES_MISSING, ERROR) \
+ FUNC(GITMODULES_BLOB, ERROR) \
+ FUNC(GITMODULES_PARSE, ERROR) \
+ FUNC(GITMODULES_NAME, ERROR) \
+ FUNC(GITMODULES_SYMLINK, ERROR) \
/* warnings */ \
FUNC(BAD_FILEMODE, WARN) \
FUNC(EMPTY_NAME, WARN) \
#undef MSG_ID
#define STR(x) #x
-#define MSG_ID(id, msg_type) { STR(id), NULL, FSCK_##msg_type },
+#define MSG_ID(id, msg_type) { STR(id), NULL, NULL, FSCK_##msg_type },
static struct {
const char *id_string;
const char *downcased;
+ const char *camelcased;
int msg_type;
} msg_id_info[FSCK_MSG_MAX + 1] = {
FOREACH_MSG_ID(MSG_ID)
- { NULL, NULL, -1 }
+ { NULL, NULL, NULL, -1 }
};
#undef MSG_ID
-static int parse_msg_id(const char *text)
+static void prepare_msg_ids(void)
{
int i;
- if (!msg_id_info[0].downcased) {
- /* convert id_string to lower case, without underscores. */
- for (i = 0; i < FSCK_MSG_MAX; i++) {
- const char *p = msg_id_info[i].id_string;
- int len = strlen(p);
- char *q = xmalloc(len);
-
- msg_id_info[i].downcased = q;
- while (*p)
- if (*p == '_')
- p++;
- else
- *(q)++ = tolower(*(p)++);
- *q = '\0';
+ if (msg_id_info[0].downcased)
+ return;
+
+ /* convert id_string to lower case, without underscores. */
+ for (i = 0; i < FSCK_MSG_MAX; i++) {
+ const char *p = msg_id_info[i].id_string;
+ int len = strlen(p);
+ char *q = xmalloc(len);
+
+ msg_id_info[i].downcased = q;
+ while (*p)
+ if (*p == '_')
+ p++;
+ else
+ *(q)++ = tolower(*(p)++);
+ *q = '\0';
+
+ p = msg_id_info[i].id_string;
+ q = xmalloc(len);
+ msg_id_info[i].camelcased = q;
+ while (*p) {
+ if (*p == '_') {
+ p++;
+ if (*p)
+ *q++ = *p++;
+ } else {
+ *q++ = tolower(*p++);
+ }
}
+ *q = '\0';
}
+}
+
+static int parse_msg_id(const char *text)
+{
+ int i;
+
+ prepare_msg_ids();
for (i = 0; i < FSCK_MSG_MAX; i++)
if (!strcmp(text, msg_id_info[i].downcased))
return -1;
}
+void list_config_fsck_msg_ids(struct string_list *list, const char *prefix)
+{
+ int i;
+
+ prepare_msg_ids();
+
+ for (i = 0; i < FSCK_MSG_MAX; i++)
+ list_config_item(list, prefix, msg_id_info[i].camelcased);
+}
+
static int fsck_msg_type(enum fsck_msg_id msg_id,
struct fsck_options *options)
{
name = get_object_name(options, &commit->object);
if (name)
- put_object_name(options, &commit->tree->object, "%s:", name);
+ put_object_name(options, &get_commit_tree(commit)->object,
+ "%s:", name);
- result = options->walk((struct object *)commit->tree, OBJ_TREE, data, options);
+ result = options->walk((struct object *)get_commit_tree(commit),
+ OBJ_TREE, data, options);
if (result < 0)
return result;
res = result;
has_empty_name |= !*name;
has_dot |= !strcmp(name, ".");
has_dotdot |= !strcmp(name, "..");
- has_dotgit |= (!strcmp(name, ".git") ||
- is_hfs_dotgit(name) ||
- is_ntfs_dotgit(name));
+ has_dotgit |= is_hfs_dotgit(name) || is_ntfs_dotgit(name);
has_zero_pad |= *(char *)desc.buffer == '0';
+
+ if (is_hfs_dotgitmodules(name) || is_ntfs_dotgitmodules(name)) {
+ if (!S_ISLNK(mode))
+ oidset_insert(&gitmodules_found, oid);
+ else
+ retval += report(options, &item->object,
+ FSCK_MSG_GITMODULES_SYMLINK,
+ ".gitmodules is a symbolic link");
+ }
+
if (update_tree_entry_gently(&desc)) {
retval += report(options, &item->object, FSCK_MSG_BAD_TREE, "cannot be parsed as a tree");
break;
static int fsck_commit_buffer(struct commit *commit, const char *buffer,
unsigned long size, struct fsck_options *options)
{
- unsigned char tree_sha1[20], sha1[20];
+ 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, &commit->object, options))
return -1;
if (!skip_prefix(buffer, "tree ", &buffer))
return report(options, &commit->object, FSCK_MSG_MISSING_TREE, "invalid format - expected 'tree' line");
- if (get_sha1_hex(buffer, tree_sha1) || buffer[40] != '\n') {
+ if (parse_oid_hex(buffer, &tree_oid, &p) || *p != '\n') {
err = report(options, &commit->object, FSCK_MSG_BAD_TREE_SHA1, "invalid 'tree' line format - bad sha1");
if (err)
return err;
}
- buffer += 41;
+ buffer = p + 1;
while (skip_prefix(buffer, "parent ", &buffer)) {
- if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n') {
+ 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 += 41;
+ buffer = p + 1;
parent_line_count++;
}
graft = lookup_commit_graft(the_repository, &commit->object.oid);
err = fsck_ident(&buffer, &commit->object, options);
if (err)
return err;
- if (!commit->tree) {
- err = report(options, &commit->object, FSCK_MSG_BAD_TREE, "could not load commit's tree %s", sha1_to_hex(tree_sha1));
+ 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;
}
static int fsck_tag_buffer(struct tag *tag, const char *data,
unsigned long size, struct fsck_options *options)
{
- unsigned char sha1[20];
+ struct object_id oid;
int ret = 0;
const char *buffer;
char *to_free = NULL, *eol;
struct strbuf sb = STRBUF_INIT;
+ const char *p;
if (data)
buffer = data;
ret = report(options, &tag->object, FSCK_MSG_MISSING_OBJECT, "invalid format - expected 'object' line");
goto done;
}
- if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n') {
+ 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 += 41;
+ buffer = p + 1;
if (!skip_prefix(buffer, "type ", &buffer)) {
ret = report(options, &tag->object, FSCK_MSG_MISSING_TYPE_ENTRY, "invalid format - expected 'type' line");
return fsck_tag_buffer(tag, data, size, options);
}
+struct fsck_gitmodules_data {
+ struct object *obj;
+ struct fsck_options *options;
+ int ret;
+};
+
+static int fsck_gitmodules_fn(const char *var, const char *value, void *vdata)
+{
+ struct fsck_gitmodules_data *data = vdata;
+ const char *subsection, *key;
+ int subsection_len;
+ char *name;
+
+ if (parse_config_key(var, "submodule", &subsection, &subsection_len, &key) < 0 ||
+ !subsection)
+ return 0;
+
+ name = xmemdupz(subsection, subsection_len);
+ if (check_submodule_name(name) < 0)
+ data->ret |= report(data->options, data->obj,
+ FSCK_MSG_GITMODULES_NAME,
+ "disallowed submodule name: %s",
+ name);
+ free(name);
+
+ return 0;
+}
+
+static int fsck_blob(struct blob *blob, const char *buf,
+ unsigned long size, struct fsck_options *options)
+{
+ struct fsck_gitmodules_data data;
+
+ if (!oidset_contains(&gitmodules_found, &blob->object.oid))
+ return 0;
+ oidset_insert(&gitmodules_done, &blob->object.oid);
+
+ if (!buf) {
+ /*
+ * A missing buffer here is a sign that the caller found the
+ * blob too gigantic to load into memory. Let's just consider
+ * that an error.
+ */
+ return report(options, &blob->object,
+ FSCK_MSG_GITMODULES_PARSE,
+ ".gitmodules too large to parse");
+ }
+
+ data.obj = &blob->object;
+ data.options = options;
+ data.ret = 0;
+ if (git_config_from_mem(fsck_gitmodules_fn, CONFIG_ORIGIN_BLOB,
+ ".gitmodules", buf, size, &data))
+ data.ret |= report(options, &blob->object,
+ FSCK_MSG_GITMODULES_PARSE,
+ "could not parse gitmodules blob");
+
+ return data.ret;
+}
+
int fsck_object(struct object *obj, void *data, unsigned long size,
struct fsck_options *options)
{
return report(options, obj, FSCK_MSG_BAD_OBJECT_SHA1, "no valid object to fsck");
if (obj->type == OBJ_BLOB)
- return 0;
+ return fsck_blob((struct blob *)obj, data, size, options);
if (obj->type == OBJ_TREE)
return fsck_tree((struct tree *) obj, options);
if (obj->type == OBJ_COMMIT)
error("object %s: %s", describe_object(o, obj), message);
return 1;
}
+
+int fsck_finish(struct fsck_options *options)
+{
+ int ret = 0;
+ struct oidset_iter iter;
+ const struct object_id *oid;
+
+ oidset_iter_init(&gitmodules_found, &iter);
+ while ((oid = oidset_iter_next(&iter))) {
+ struct blob *blob;
+ enum object_type type;
+ unsigned long size;
+ char *buf;
+
+ if (oidset_contains(&gitmodules_done, oid))
+ continue;
+
+ blob = lookup_blob(oid);
+ if (!blob) {
+ struct object *obj = lookup_unknown_object(oid->hash);
+ 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(&blob->object.oid))
+ continue;
+ ret |= report(options, &blob->object,
+ FSCK_MSG_GITMODULES_MISSING,
+ "unable to read .gitmodules blob");
+ continue;
+ }
+
+ if (type == OBJ_BLOB)
+ ret |= fsck_blob(blob, buf, size, options);
+ else
+ ret |= report(options, &blob->object,
+ FSCK_MSG_GITMODULES_BLOB,
+ "non-blob found at .gitmodules");
+ free(buf);
+ }
+
+
+ oidset_clear(&gitmodules_found);
+ oidset_clear(&gitmodules_done);
+ return ret;
+}