X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=fsck.c;h=797e3178ae279f444d2efa7e3758652ad0898dd7;hb=fae09a8084c9b51632726523b477a78dd28d7d7e;hp=558f0a6b349b921802668282e8c2b71b0f03d754;hpb=df391b192d285646fe80ca4d3cfe3c0a3beb5989;p=git.git diff --git a/fsck.c b/fsck.c index 558f0a6b3..797e3178a 100644 --- a/fsck.c +++ b/fsck.c @@ -1,164 +1,92 @@ #include "cache.h" -#include "commit.h" -#include "tree.h" +#include "object.h" #include "blob.h" -#include "tag.h" -#include "refs.h" -#include "pack.h" -#include "cache-tree.h" +#include "tree.h" #include "tree-walk.h" +#include "commit.h" +#include "tag.h" +#include "fsck.h" -#define REACHABLE 0x0001 -#define SEEN 0x0002 - -static int show_root; -static int show_tags; -static int show_unreachable; -static int check_full; -static int check_strict; -static int keep_cache_objects; -static unsigned char head_sha1[20]; - -#ifdef NO_D_INO_IN_DIRENT -#define SORT_DIRENT 0 -#define DIRENT_SORT_HINT(de) 0 -#else -#define SORT_DIRENT 1 -#define DIRENT_SORT_HINT(de) ((de)->d_ino) -#endif - -static void objreport(struct object *obj, const char *severity, - const char *err, va_list params) -{ - fprintf(stderr, "%s in %s %s: ", - severity, typename(obj->type), sha1_to_hex(obj->sha1)); - vfprintf(stderr, err, params); - fputs("\n", stderr); -} - -static int objerror(struct object *obj, const char *err, ...) -{ - va_list params; - va_start(params, err); - objreport(obj, "error", err, params); - va_end(params); - return -1; -} - -static int objwarning(struct object *obj, const char *err, ...) +static int fsck_walk_tree(struct tree *tree, fsck_walk_func walk, void *data) { - va_list params; - va_start(params, err); - objreport(obj, "warning", err, params); - va_end(params); - return -1; -} + struct tree_desc desc; + struct name_entry entry; + int res = 0; -/* - * Check a single reachable object - */ -static void check_reachable_object(struct object *obj) -{ - const struct object_refs *refs; + if (parse_tree(tree)) + return -1; - /* - * We obviously want the object to be parsed, - * except if it was in a pack-file and we didn't - * do a full fsck - */ - if (!obj->parsed) { - if (has_sha1_file(obj->sha1)) - return; /* it is in pack - forget about it */ - printf("missing %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1)); - return; - } + init_tree_desc(&desc, tree->buffer, tree->size); + while (tree_entry(&desc, &entry)) { + int result; - /* - * Check that everything that we try to reference is also good. - */ - refs = lookup_object_refs(obj); - if (refs) { - unsigned j; - for (j = 0; j < refs->count; j++) { - struct object *ref = refs->ref[j]; - if (ref->parsed || - (has_sha1_file(ref->sha1))) - continue; - printf("broken link from %7s %s\n", - typename(obj->type), sha1_to_hex(obj->sha1)); - printf(" to %7s %s\n", - typename(ref->type), sha1_to_hex(ref->sha1)); + if (S_ISGITLINK(entry.mode)) + continue; + if (S_ISDIR(entry.mode)) + result = walk(&lookup_tree(entry.sha1)->object, OBJ_TREE, data); + else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) + result = walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data); + else { + result = error("in tree %s: entry %s has bad mode %.6o\n", + sha1_to_hex(tree->object.sha1), entry.path, entry.mode); } + if (result < 0) + return result; + if (!res) + res = result; } + return res; } -/* - * Check a single unreachable object - */ -static void check_unreachable_object(struct object *obj) +static int fsck_walk_commit(struct commit *commit, fsck_walk_func walk, void *data) { - /* - * Missing unreachable object? Ignore it. It's not like - * we miss it (since it can't be reached), nor do we want - * to complain about it being unreachable (since it does - * not exist). - */ - if (!obj->parsed) - return; - - /* - * Unreachable object that exists? Show it if asked to, - * since this is something that is prunable. - */ - if (show_unreachable) { - printf("unreachable %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1)); - return; - } - - /* - * "!used" means that nothing at all points to it, including - * other unreacahble objects. In other words, it's the "tip" - * of some set of unreachable objects, usually a commit that - * got dropped. - * - * Such starting points are more interesting than some random - * set of unreachable objects, so we show them even if the user - * hasn't asked for _all_ unreachable objects. If you have - * deleted a branch by mistake, this is a prime candidate to - * start looking at, for example. - */ - if (!obj->used) { - printf("dangling %s %s\n", typename(obj->type), - sha1_to_hex(obj->sha1)); - return; + struct commit_list *parents; + int res; + int result; + + if (parse_commit(commit)) + return -1; + + result = walk((struct object *)commit->tree, OBJ_TREE, data); + if (result < 0) + return result; + res = result; + + parents = commit->parents; + while (parents) { + result = walk((struct object *)parents->item, OBJ_COMMIT, data); + if (result < 0) + return result; + if (!res) + res = result; + parents = parents->next; } - - /* - * Otherwise? It's there, it's unreachable, and some other unreachable - * object points to it. Ignore it - it's not interesting, and we showed - * all the interesting cases above. - */ + return res; } -static void check_object(struct object *obj) +static int fsck_walk_tag(struct tag *tag, fsck_walk_func walk, void *data) { - if (obj->flags & REACHABLE) - check_reachable_object(obj); - else - check_unreachable_object(obj); + if (parse_tag(tag)) + return -1; + return walk(tag->tagged, OBJ_ANY, data); } -static void check_connectivity(void) +int fsck_walk(struct object *obj, fsck_walk_func walk, void *data) { - int i, max; - - /* Look up all the requirements, warn about missing objects.. */ - max = get_max_object_index(); - for (i = 0; i < max; i++) { - struct object *obj = get_indexed_object(i); - - if (obj) - check_object(obj); + if (!obj) + return -1; + switch (obj->type) { + case OBJ_BLOB: + return 0; + case OBJ_TREE: + return fsck_walk_tree((struct tree *)obj, walk, data); + case OBJ_COMMIT: + return fsck_walk_commit((struct commit *)obj, walk, data); + case OBJ_TAG: + return fsck_walk_tag((struct tag *)obj, walk, data); + default: + error("Unknown object type for %s", sha1_to_hex(obj->sha1)); + return -1; } } @@ -208,10 +136,11 @@ static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, con return c1 < c2 ? 0 : TREE_UNORDERED; } -static int fsck_tree(struct tree *item) +static int fsck_tree(struct tree *item, int strict, fsck_error error_func) { int retval; int has_full_path = 0; + int has_empty_name = 0; int has_zero_pad = 0; int has_bad_modes = 0; int has_dup_entries = 0; @@ -221,12 +150,12 @@ static int fsck_tree(struct tree *item) const char *o_name; const unsigned char *o_sha1; - desc.buf = item->buffer; - desc.size = item->size; + init_tree_desc(&desc, item->buffer, item->size); o_mode = 0; o_name = NULL; o_sha1 = NULL; + while (desc.size) { unsigned mode; const char *name; @@ -236,7 +165,9 @@ static int fsck_tree(struct tree *item) if (strchr(name, '/')) has_full_path = 1; - has_zero_pad |= *(char *)desc.buf == '0'; + if (!*name) + has_empty_name = 1; + has_zero_pad |= *(char *)desc.buffer == '0'; update_tree_entry(&desc); switch (mode) { @@ -247,6 +178,7 @@ static int fsck_tree(struct tree *item) case S_IFREG | 0644: case S_IFLNK: case S_IFDIR: + case S_IFGITLINK: break; /* * This is nonstandard, but we had a few of these @@ -254,7 +186,7 @@ static int fsck_tree(struct tree *item) * bits.. */ case S_IFREG | 0664: - if (!check_strict) + if (!strict) break; default: has_bad_modes = 1; @@ -277,414 +209,125 @@ static int fsck_tree(struct tree *item) o_name = name; o_sha1 = sha1; } - free(item->buffer); - item->buffer = NULL; retval = 0; - if (has_full_path) { - objwarning(&item->object, "contains full pathnames"); - } - if (has_zero_pad) { - objwarning(&item->object, "contains zero-padded file modes"); - } - if (has_bad_modes) { - objwarning(&item->object, "contains bad file modes"); - } - if (has_dup_entries) { - retval = objerror(&item->object, "contains duplicate file entries"); - } - if (not_properly_sorted) { - retval = objerror(&item->object, "not properly sorted"); - } + if (has_full_path) + retval += error_func(&item->object, FSCK_WARN, "contains full pathnames"); + if (has_empty_name) + retval += error_func(&item->object, FSCK_WARN, "contains empty pathname"); + if (has_zero_pad) + retval += error_func(&item->object, FSCK_WARN, "contains zero-padded file modes"); + if (has_bad_modes) + retval += error_func(&item->object, FSCK_WARN, "contains bad file modes"); + if (has_dup_entries) + retval += error_func(&item->object, FSCK_ERROR, "contains duplicate file entries"); + if (not_properly_sorted) + retval += error_func(&item->object, FSCK_ERROR, "not properly sorted"); return retval; } -static int fsck_commit(struct commit *commit) +static int fsck_commit(struct commit *commit, fsck_error error_func) { char *buffer = commit->buffer; unsigned char tree_sha1[20], sha1[20]; + struct commit_graft *graft; + int parents = 0; + + if (!commit->date) + return error_func(&commit->object, FSCK_ERROR, "invalid author/committer line"); if (memcmp(buffer, "tree ", 5)) - return objerror(&commit->object, "invalid format - expected 'tree' line"); + return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'tree' line"); if (get_sha1_hex(buffer+5, tree_sha1) || buffer[45] != '\n') - return objerror(&commit->object, "invalid 'tree' line format - bad sha1"); + return error_func(&commit->object, FSCK_ERROR, "invalid 'tree' line format - bad sha1"); buffer += 46; while (!memcmp(buffer, "parent ", 7)) { if (get_sha1_hex(buffer+7, sha1) || buffer[47] != '\n') - return objerror(&commit->object, "invalid 'parent' line format - bad sha1"); + return error_func(&commit->object, FSCK_ERROR, "invalid 'parent' line format - bad sha1"); buffer += 48; + parents++; + } + graft = lookup_commit_graft(commit->object.sha1); + if (graft) { + struct commit_list *p = commit->parents; + parents = 0; + while (p) { + p = p->next; + parents++; + } + if (graft->nr_parent == -1 && !parents) + ; /* shallow commit */ + else if (graft->nr_parent != parents) + return error_func(&commit->object, FSCK_ERROR, "graft objects missing"); + } else { + struct commit_list *p = commit->parents; + while (p && parents) { + p = p->next; + parents--; + } + if (p || parents) + return error_func(&commit->object, FSCK_ERROR, "parent objects missing"); } if (memcmp(buffer, "author ", 7)) - return objerror(&commit->object, "invalid format - expected 'author' line"); - free(commit->buffer); - commit->buffer = NULL; + return error_func(&commit->object, FSCK_ERROR, "invalid format - expected 'author' line"); if (!commit->tree) - return objerror(&commit->object, "could not load commit's tree %s", tree_sha1); - if (!commit->parents && show_root) - printf("root %s\n", sha1_to_hex(commit->object.sha1)); - if (!commit->date) - printf("bad commit date in %s\n", - sha1_to_hex(commit->object.sha1)); + return error_func(&commit->object, FSCK_ERROR, "could not load commit's tree %s", sha1_to_hex(tree_sha1)); + return 0; } -static int fsck_tag(struct tag *tag) +static int fsck_tag(struct tag *tag, fsck_error error_func) { struct object *tagged = tag->tagged; - if (!tagged) { - return objerror(&tag->object, "could not load tagged object"); - } - if (!show_tags) - return 0; - - printf("tagged %s %s", typename(tagged->type), sha1_to_hex(tagged->sha1)); - printf(" (%s) in %s\n", tag->tag, sha1_to_hex(tag->object.sha1)); + if (!tagged) + return error_func(&tag->object, FSCK_ERROR, "could not load tagged object"); return 0; } -static int fsck_sha1(unsigned char *sha1) +int fsck_object(struct object *obj, int strict, fsck_error error_func) { - struct object *obj = parse_object(sha1); if (!obj) - return error("%s: object corrupt or missing", sha1_to_hex(sha1)); - if (obj->flags & SEEN) - return 0; - obj->flags |= SEEN; + return error_func(obj, FSCK_ERROR, "no valid object to fsck"); + if (obj->type == OBJ_BLOB) return 0; if (obj->type == OBJ_TREE) - return fsck_tree((struct tree *) obj); + return fsck_tree((struct tree *) obj, strict, error_func); if (obj->type == OBJ_COMMIT) - return fsck_commit((struct commit *) obj); + return fsck_commit((struct commit *) obj, error_func); if (obj->type == OBJ_TAG) - return fsck_tag((struct tag *) obj); - /* By now, parse_object() would've returned NULL instead. */ - return objerror(obj, "unknown type '%d' (internal fsck error)", obj->type); -} + return fsck_tag((struct tag *) obj, error_func); -/* - * This is the sorting chunk size: make it reasonably - * big so that we can sort well.. - */ -#define MAX_SHA1_ENTRIES (1024) - -struct sha1_entry { - unsigned long ino; - unsigned char sha1[20]; -}; - -static struct { - unsigned long nr; - struct sha1_entry *entry[MAX_SHA1_ENTRIES]; -} sha1_list; - -static int ino_compare(const void *_a, const void *_b) -{ - const struct sha1_entry *a = _a, *b = _b; - unsigned long ino1 = a->ino, ino2 = b->ino; - return ino1 < ino2 ? -1 : ino1 > ino2 ? 1 : 0; -} - -static void fsck_sha1_list(void) -{ - int i, nr = sha1_list.nr; - - if (SORT_DIRENT) - qsort(sha1_list.entry, nr, - sizeof(struct sha1_entry *), ino_compare); - for (i = 0; i < nr; i++) { - struct sha1_entry *entry = sha1_list.entry[i]; - unsigned char *sha1 = entry->sha1; - - sha1_list.entry[i] = NULL; - fsck_sha1(sha1); - free(entry); - } - sha1_list.nr = 0; + return error_func(obj, FSCK_ERROR, "unknown type '%d' (internal fsck error)", + obj->type); } -static void add_sha1_list(unsigned char *sha1, unsigned long ino) +int fsck_error_function(struct object *obj, int type, const char *fmt, ...) { - struct sha1_entry *entry = xmalloc(sizeof(*entry)); - int nr; - - entry->ino = ino; - hashcpy(entry->sha1, sha1); - nr = sha1_list.nr; - if (nr == MAX_SHA1_ENTRIES) { - fsck_sha1_list(); - nr = 0; + va_list ap; + int len; + struct strbuf sb; + + strbuf_init(&sb, 0); + strbuf_addf(&sb, "object %s:", obj->sha1?sha1_to_hex(obj->sha1):"(null)"); + + va_start(ap, fmt); + len = vsnprintf(sb.buf + sb.len, strbuf_avail(&sb), fmt, ap); + va_end(ap); + + if (len < 0) + len = 0; + if (len >= strbuf_avail(&sb)) { + strbuf_grow(&sb, len + 2); + va_start(ap, fmt); + len = vsnprintf(sb.buf + sb.len, strbuf_avail(&sb), fmt, ap); + va_end(ap); + if (len >= strbuf_avail(&sb)) + die("this should not happen, your snprintf is broken"); } - sha1_list.entry[nr] = entry; - sha1_list.nr = ++nr; -} - -static void fsck_dir(int i, char *path) -{ - DIR *dir = opendir(path); - struct dirent *de; - - if (!dir) - return; - - while ((de = readdir(dir)) != NULL) { - char name[100]; - unsigned char sha1[20]; - int len = strlen(de->d_name); - switch (len) { - case 2: - if (de->d_name[1] != '.') - break; - case 1: - if (de->d_name[0] != '.') - break; - continue; - case 38: - sprintf(name, "%02x", i); - memcpy(name+2, de->d_name, len+1); - if (get_sha1_hex(name, sha1) < 0) - break; - add_sha1_list(sha1, DIRENT_SORT_HINT(de)); - continue; - } - fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name); - } - closedir(dir); -} - -static int default_refs; - -static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1, - const char *email, unsigned long timestamp, int tz, - const char *message, void *cb_data) -{ - struct object *obj; - - if (!is_null_sha1(osha1)) { - obj = lookup_object(osha1); - if (obj) { - obj->used = 1; - mark_reachable(obj, REACHABLE); - } - } - obj = lookup_object(nsha1); - if (obj) { - obj->used = 1; - mark_reachable(obj, REACHABLE); - } - return 0; -} - -static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) -{ - struct object *obj; - - obj = lookup_object(sha1); - if (!obj) { - if (has_sha1_file(sha1)) { - default_refs++; - return 0; /* it is in a pack */ - } - error("%s: invalid sha1 pointer %s", refname, sha1_to_hex(sha1)); - /* We'll continue with the rest despite the error.. */ - return 0; - } - default_refs++; - obj->used = 1; - mark_reachable(obj, REACHABLE); - - for_each_reflog_ent(refname, fsck_handle_reflog_ent, NULL); - - return 0; -} - -static void get_default_heads(void) -{ - for_each_ref(fsck_handle_ref, NULL); - - /* - * Not having any default heads isn't really fatal, but - * it does mean that "--unreachable" no longer makes any - * sense (since in this case everything will obviously - * be unreachable by definition. - * - * Showing dangling objects is valid, though (as those - * dangling objects are likely lost heads). - * - * So we just print a warning about it, and clear the - * "show_unreachable" flag. - */ - if (!default_refs) { - error("No default references"); - show_unreachable = 0; - } -} - -static void fsck_object_dir(const char *path) -{ - int i; - for (i = 0; i < 256; i++) { - static char dir[4096]; - sprintf(dir, "%s/%02x", path, i); - fsck_dir(i, dir); - } - fsck_sha1_list(); -} - -static int fsck_head_link(void) -{ - unsigned char sha1[20]; - int flag; - const char *head_points_at = resolve_ref("HEAD", sha1, 1, &flag); - - if (!head_points_at || !(flag & REF_ISSYMREF)) - return error("HEAD is not a symbolic ref"); - if (strncmp(head_points_at, "refs/heads/", 11)) - return error("HEAD points to something strange (%s)", - head_points_at); - if (is_null_sha1(sha1)) - return error("HEAD: not a valid git pointer"); - return 0; -} - -static int fsck_cache_tree(struct cache_tree *it) -{ - int i; - int err = 0; - - if (0 <= it->entry_count) { - struct object *obj = parse_object(it->sha1); - if (!obj) { - error("%s: invalid sha1 pointer in cache-tree", - sha1_to_hex(it->sha1)); - return 1; - } - mark_reachable(obj, REACHABLE); - obj->used = 1; - if (obj->type != OBJ_TREE) - err |= objerror(obj, "non-tree in cache-tree"); - } - for (i = 0; i < it->subtree_nr; i++) - err |= fsck_cache_tree(it->down[i]->cache_tree); - return err; -} - -int main(int argc, char **argv) -{ - int i, heads; - - track_object_refs = 1; - setup_git_directory(); - - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (!strcmp(arg, "--unreachable")) { - show_unreachable = 1; - continue; - } - if (!strcmp(arg, "--tags")) { - show_tags = 1; - continue; - } - if (!strcmp(arg, "--root")) { - show_root = 1; - continue; - } - if (!strcmp(arg, "--cache")) { - keep_cache_objects = 1; - continue; - } - if (!strcmp(arg, "--full")) { - check_full = 1; - continue; - } - if (!strcmp(arg, "--strict")) { - check_strict = 1; - continue; - } - if (*arg == '-') - usage("git-fsck [--tags] [--root] [[--unreachable] [--cache] [--full] [--strict] *]"); - } - - fsck_head_link(); - fsck_object_dir(get_object_directory()); - if (check_full) { - struct alternate_object_database *alt; - struct packed_git *p; - prepare_alt_odb(); - for (alt = alt_odb_list; alt; alt = alt->next) { - char namebuf[PATH_MAX]; - int namelen = alt->name - alt->base; - memcpy(namebuf, alt->base, namelen); - namebuf[namelen - 1] = 0; - fsck_object_dir(namebuf); - } - prepare_packed_git(); - for (p = packed_git; p; p = p->next) - /* verify gives error messages itself */ - verify_pack(p, 0); - - for (p = packed_git; p; p = p->next) { - int num = num_packed_objects(p); - for (i = 0; i < num; i++) { - unsigned char sha1[20]; - nth_packed_object_sha1(p, i, sha1); - fsck_sha1(sha1); - } - } - } - - heads = 0; - for (i = 1; i < argc; i++) { - const char *arg = argv[i]; - - if (*arg == '-') - continue; - - if (!get_sha1(arg, head_sha1)) { - struct object *obj = lookup_object(head_sha1); - - /* Error is printed by lookup_object(). */ - if (!obj) - continue; - - obj->used = 1; - mark_reachable(obj, REACHABLE); - heads++; - continue; - } - error("invalid parameter: expected sha1, got '%s'", arg); - } - - /* - * If we've not been given any explicit head information, do the - * default ones from .git/refs. We also consider the index file - * in this case (ie this implies --cache). - */ - if (!heads) { - get_default_heads(); - keep_cache_objects = 1; - } - - if (keep_cache_objects) { - int i; - read_cache(); - for (i = 0; i < active_nr; i++) { - struct blob *blob = lookup_blob(active_cache[i]->sha1); - struct object *obj; - if (!blob) - continue; - obj = &blob->object; - obj->used = 1; - mark_reachable(obj, REACHABLE); - } - if (active_cache_tree) - fsck_cache_tree(active_cache_tree); - } - - check_connectivity(); - return 0; + error(sb.buf); + strbuf_release(&sb); + return 1; }