diff options
author | Junio C Hamano <gitster@pobox.com> | 2008-03-02 15:11:07 -0800 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2008-03-02 15:11:07 -0800 |
commit | eadbcd498a18d60467883aeb6c0726048e7382a2 (patch) | |
tree | 50e765722a06a56121c6431d47cd319a2594ae71 /builtin-fsck.c | |
parent | c42f63671c0f09cba0c8247c4d1d8121f37798fe (diff) | |
parent | 28f72a0f232dfc71b3be726e7e71d0a6d5f9ebba (diff) | |
download | git-eadbcd498a18d60467883aeb6c0726048e7382a2.tar.gz |
Merge branch 'mk/maint-parse-careful'
* mk/maint-parse-careful:
receive-pack: use strict mode for unpacking objects
index-pack: introduce checking mode
unpack-objects: prevent writing of inconsistent objects
unpack-object: cache for non written objects
add common fsck error printing function
builtin-fsck: move common object checking code to fsck.c
builtin-fsck: reports missing parent commits
Remove unused object-ref code
builtin-fsck: move away from object-refs to fsck_walk
add generic, type aware object chain walker
Conflicts:
Makefile
builtin-fsck.c
Diffstat (limited to 'builtin-fsck.c')
-rw-r--r-- | builtin-fsck.c | 341 |
1 files changed, 104 insertions, 237 deletions
diff --git a/builtin-fsck.c b/builtin-fsck.c index cc7524be80..78a6e1ff71 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -8,6 +8,7 @@ #include "pack.h" #include "cache-tree.h" #include "tree-walk.h" +#include "fsck.h" #include "parse-options.h" #define REACHABLE 0x0001 @@ -54,13 +55,75 @@ static int objerror(struct object *obj, const char *err, ...) return -1; } -static int objwarning(struct object *obj, const char *err, ...) +static int fsck_error_func(struct object *obj, int type, const char *err, ...) { va_list params; va_start(params, err); - objreport(obj, "warning", err, params); + objreport(obj, (type == FSCK_WARN) ? "warning" : "error", err, params); va_end(params); - return -1; + return (type == FSCK_WARN) ? 0 : 1; +} + +static int mark_object(struct object *obj, int type, void *data) +{ + struct tree *tree = NULL; + struct object *parent = data; + int result; + + if (!obj) { + printf("broken link from %7s %s\n", + typename(parent->type), sha1_to_hex(parent->sha1)); + printf("broken link from %7s %s\n", + (type == OBJ_ANY ? "unknown" : typename(type)), "unknown"); + errors_found |= ERROR_REACHABLE; + return 1; + } + + if (type != OBJ_ANY && obj->type != type) + objerror(parent, "wrong object type in link"); + + if (obj->flags & REACHABLE) + return 0; + obj->flags |= REACHABLE; + if (!obj->parsed) { + if (parent && !has_sha1_file(obj->sha1)) { + printf("broken link from %7s %s\n", + typename(parent->type), sha1_to_hex(parent->sha1)); + printf(" to %7s %s\n", + typename(obj->type), sha1_to_hex(obj->sha1)); + errors_found |= ERROR_REACHABLE; + } + return 1; + } + + if (obj->type == OBJ_TREE) { + obj->parsed = 0; + tree = (struct tree *)obj; + if (parse_tree(tree) < 0) + return 1; /* error already displayed */ + } + result = fsck_walk(obj, mark_object, obj); + if (tree) { + free(tree->buffer); + tree->buffer = NULL; + } + if (result < 0) + result = 1; + + return result; +} + +static void mark_object_reachable(struct object *obj) +{ + mark_object(obj, OBJ_ANY, 0); +} + +static int mark_used(struct object *obj, int type, void *data) +{ + if (!obj) + return 1; + obj->used = 1; + return 0; } /* @@ -68,8 +131,6 @@ static int objwarning(struct object *obj, const char *err, ...) */ static void check_reachable_object(struct object *obj) { - const struct object_refs *refs; - /* * We obviously want the object to be parsed, * except if it was in a pack-file and we didn't @@ -82,25 +143,6 @@ static void check_reachable_object(struct object *obj) errors_found |= ERROR_REACHABLE; return; } - - /* - * 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)); - errors_found |= ERROR_REACHABLE; - } - } } /* @@ -204,230 +246,56 @@ static void check_connectivity(void) } } -/* - * The entries in a tree are ordered in the _path_ order, - * which means that a directory entry is ordered by adding - * a slash to the end of it. - * - * So a directory called "a" is ordered _after_ a file - * called "a.c", because "a/" sorts after "a.c". - */ -#define TREE_UNORDERED (-1) -#define TREE_HAS_DUPS (-2) - -static int verify_ordered(unsigned mode1, const char *name1, unsigned mode2, const char *name2) +static int fsck_sha1(const unsigned char *sha1) { - int len1 = strlen(name1); - int len2 = strlen(name2); - int len = len1 < len2 ? len1 : len2; - unsigned char c1, c2; - int cmp; - - cmp = memcmp(name1, name2, len); - if (cmp < 0) + struct object *obj = parse_object(sha1); + if (!obj) { + errors_found |= ERROR_OBJECT; + return error("%s: object corrupt or missing", + sha1_to_hex(sha1)); + } + if (obj->flags & SEEN) return 0; - if (cmp > 0) - return TREE_UNORDERED; - - /* - * Ok, the first <len> characters are the same. - * Now we need to order the next one, but turn - * a '\0' into a '/' for a directory entry. - */ - c1 = name1[len]; - c2 = name2[len]; - if (!c1 && !c2) - /* - * git-write-tree used to write out a nonsense tree that has - * entries with the same name, one blob and one tree. Make - * sure we do not have duplicate entries. - */ - return TREE_HAS_DUPS; - if (!c1 && S_ISDIR(mode1)) - c1 = '/'; - if (!c2 && S_ISDIR(mode2)) - c2 = '/'; - return c1 < c2 ? 0 : TREE_UNORDERED; -} - -static int fsck_tree(struct tree *item) -{ - 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; - int not_properly_sorted = 0; - struct tree_desc desc; - unsigned o_mode; - const char *o_name; - const unsigned char *o_sha1; + obj->flags |= SEEN; if (verbose) - fprintf(stderr, "Checking tree %s\n", - sha1_to_hex(item->object.sha1)); - - 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; - const unsigned char *sha1; - - sha1 = tree_entry_extract(&desc, &name, &mode); - - if (strchr(name, '/')) - has_full_path = 1; - if (!*name) - has_empty_name = 1; - has_zero_pad |= *(char *)desc.buffer == '0'; - update_tree_entry(&desc); - - switch (mode) { - /* - * Standard modes.. - */ - case S_IFREG | 0755: - case S_IFREG | 0644: - case S_IFLNK: - case S_IFDIR: - case S_IFGITLINK: - break; - /* - * This is nonstandard, but we had a few of these - * early on when we honored the full set of mode - * bits.. - */ - case S_IFREG | 0664: - if (!check_strict) - break; - default: - has_bad_modes = 1; - } + fprintf(stderr, "Checking %s %s\n", + typename(obj->type), sha1_to_hex(obj->sha1)); - if (o_name) { - switch (verify_ordered(o_mode, o_name, mode, name)) { - case TREE_UNORDERED: - not_properly_sorted = 1; - break; - case TREE_HAS_DUPS: - has_dup_entries = 1; - break; - default: - break; - } - } + if (fsck_walk(obj, mark_used, 0)) + objerror(obj, "broken links"); + if (fsck_object(obj, check_strict, fsck_error_func)) + return -1; - o_mode = mode; - o_name = name; - o_sha1 = sha1; - } - free(item->buffer); - item->buffer = NULL; + if (obj->type == OBJ_TREE) { + struct tree *item = (struct tree *) obj; - retval = 0; - if (has_full_path) { - objwarning(&item->object, "contains full pathnames"); + free(item->buffer); + item->buffer = NULL; } - if (has_empty_name) { - objwarning(&item->object, "contains empty pathname"); - } - 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"); - } - return retval; -} -static int fsck_commit(struct commit *commit) -{ - char *buffer = commit->buffer; - unsigned char tree_sha1[20], sha1[20]; + if (obj->type == OBJ_COMMIT) { + struct commit *commit = (struct commit *) obj; - if (verbose) - fprintf(stderr, "Checking commit %s\n", - sha1_to_hex(commit->object.sha1)); - - if (!commit->date) - return objerror(&commit->object, "invalid author/committer line"); - - if (memcmp(buffer, "tree ", 5)) - return objerror(&commit->object, "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"); - 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"); - buffer += 48; - } - if (memcmp(buffer, "author ", 7)) - return objerror(&commit->object, "invalid format - expected 'author' line"); - free(commit->buffer); - commit->buffer = NULL; - 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)); - return 0; -} + free(commit->buffer); + commit->buffer = NULL; -static int fsck_tag(struct tag *tag) -{ - struct object *tagged = tag->tagged; + if (!commit->parents && show_root) + printf("root %s\n", sha1_to_hex(commit->object.sha1)); + } - if (verbose) - fprintf(stderr, "Checking tag %s\n", - sha1_to_hex(tag->object.sha1)); + if (obj->type == OBJ_TAG) { + struct tag *tag = (struct tag *) obj; - if (!tagged) { - return objerror(&tag->object, "could not load tagged object"); + if (show_tags && tag->tagged) { + printf("tagged %s %s", typename(tag->tagged->type), sha1_to_hex(tag->tagged->sha1)); + printf(" (%s) in %s\n", tag->tag, sha1_to_hex(tag->object.sha1)); + } } - 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)); return 0; } -static int fsck_sha1(const unsigned char *sha1) -{ - struct object *obj = parse_object(sha1); - if (!obj) { - errors_found |= ERROR_OBJECT; - return error("%s: object corrupt or missing", - sha1_to_hex(sha1)); - } - if (obj->flags & SEEN) - return 0; - obj->flags |= SEEN; - if (obj->type == OBJ_BLOB) - return 0; - if (obj->type == OBJ_TREE) - return fsck_tree((struct tree *) obj); - if (obj->type == OBJ_COMMIT) - return fsck_commit((struct commit *) obj); - 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); -} - /* * This is the sorting chunk size: make it reasonably * big so that we can sort well.. @@ -538,13 +406,13 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1, obj = lookup_object(osha1); if (obj) { obj->used = 1; - mark_reachable(obj, REACHABLE); + mark_object_reachable(obj); } } obj = lookup_object(nsha1); if (obj) { obj->used = 1; - mark_reachable(obj, REACHABLE); + mark_object_reachable(obj); } return 0; } @@ -574,7 +442,7 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int f error("%s: not a commit", refname); default_refs++; obj->used = 1; - mark_reachable(obj, REACHABLE); + mark_object_reachable(obj); return 0; } @@ -660,7 +528,7 @@ static int fsck_cache_tree(struct cache_tree *it) sha1_to_hex(it->sha1)); return 1; } - mark_reachable(obj, REACHABLE); + mark_object_reachable(obj); obj->used = 1; if (obj->type != OBJ_TREE) err |= objerror(obj, "non-tree in cache-tree"); @@ -693,7 +561,6 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) { int i, heads; - track_object_refs = 1; errors_found = 0; argc = parse_options(argc, argv, fsck_opts, fsck_usage, 0); @@ -741,7 +608,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) continue; obj->used = 1; - mark_reachable(obj, REACHABLE); + mark_object_reachable(obj); heads++; continue; } @@ -773,7 +640,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix) continue; obj = &blob->object; obj->used = 1; - mark_reachable(obj, REACHABLE); + mark_object_reachable(obj); } if (active_cache_tree) fsck_cache_tree(active_cache_tree); |