diff options
| -rw-r--r-- | convert-cache.c | 2 | ||||
| -rw-r--r-- | fsck-cache.c | 149 | ||||
| -rw-r--r-- | tree.c | 5 | ||||
| -rw-r--r-- | tree.h | 1 |
4 files changed, 132 insertions, 25 deletions
diff --git a/convert-cache.c b/convert-cache.c index 4d34d2d57e..93e477ad3b 100644 --- a/convert-cache.c +++ b/convert-cache.c @@ -99,7 +99,7 @@ static int write_subdirectory(void *buffer, unsigned long size, const char *base continue; } - newlen += sprintf(new + newlen, "%o %.*s", S_IFDIR, slash - path, path); + newlen += sprintf(new + newlen, "%o %.*s", S_IFDIR, (int)(slash - path), path); new[newlen++] = 0; sha1 = (unsigned char *)(new + newlen); newlen += 20; diff --git a/fsck-cache.c b/fsck-cache.c index fbcd2b64f4..637aededbd 100644 --- a/fsck-cache.c +++ b/fsck-cache.c @@ -34,7 +34,7 @@ static void check_connectivity(void) printf("broken link from %7s %s\n", obj->type, sha1_to_hex(obj->sha1)); printf(" to %7s %s\n", - obj->type, sha1_to_hex(refs->item->sha1)); + refs->item->type, sha1_to_hex(refs->item->sha1)); } /* Don't bother with tag reachability. */ @@ -53,13 +53,69 @@ 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". + */ +static int verify_ordered(struct tree_entry_list *a, struct tree_entry_list *b) +{ + int len1 = strlen(a->name); + int len2 = strlen(b->name); + int len = len1 < len2 ? len1 : len2; + unsigned char c1, c2; + int cmp; + + cmp = memcmp(a->name, b->name, len); + if (cmp < 0) + return 0; + if (cmp > 0) + return -1; + + /* + * 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 = a->name[len]; + c2 = b->name[len]; + if (!c1 && a->directory) + c1 = '/'; + if (!c2 && b->directory) + c2 = '/'; + return c1 < c2 ? 0 : -1; +} + static int fsck_tree(struct tree *item) { - if (item->has_full_path) { + int has_full_path = 0; + struct tree_entry_list *entry, *last; + + last = NULL; + for (entry = item->entries; entry; entry = entry->next) { + if (strchr(entry->name, '/')) + has_full_path = 1; + + if (last) { + if (verify_ordered(last, entry) < 0) { + fprintf(stderr, "tree %s not ordered\n", + sha1_to_hex(item->object.sha1)); + return -1; + } + } + + last = entry; + } + + if (has_full_path) { fprintf(stderr, "warning: fsck-cache: tree %s " "has full pathnames in it\n", sha1_to_hex(item->object.sha1)); } + return 0; } @@ -88,23 +144,76 @@ static int fsck_tag(struct tag *tag) return 0; } -static int fsck_name(char *hex) +static int fsck_sha1(unsigned char *sha1) { + struct object *obj = parse_object(sha1); + if (!obj) + return -1; + if (obj->type == blob_type) + return 0; + if (obj->type == tree_type) + return fsck_tree((struct tree *) obj); + if (obj->type == commit_type) + return fsck_commit((struct commit *) obj); + if (obj->type == tag_type) + return fsck_tag((struct tag *) obj); + return -1; +} + +/* + * 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]; - if (!get_sha1_hex(hex, sha1)) { - struct object *obj = parse_object(sha1); - if (!obj) - return -1; - if (obj->type == blob_type) - return 0; - if (obj->type == tree_type) - return fsck_tree((struct tree *) obj); - if (obj->type == commit_type) - return fsck_commit((struct commit *) obj); - if (obj->type == tag_type) - return fsck_tag((struct tag *) obj); +}; + +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; + + 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; + if (fsck_sha1(sha1) < 0) + fprintf(stderr, "bad sha1 entry '%s'\n", sha1_to_hex(sha1)); + free(entry); } - return -1; + sha1_list.nr = 0; +} + +static void add_sha1_list(unsigned char *sha1, unsigned long ino) +{ + struct sha1_entry *entry = xmalloc(sizeof(*entry)); + int nr; + + entry->ino = ino; + memcpy(entry->sha1, sha1, 20); + nr = sha1_list.nr; + if (nr == MAX_SHA1_ENTRIES) { + fsck_sha1_list(); + nr = 0; + } + sha1_list.entry[nr] = entry; + sha1_list.nr = ++nr; } static int fsck_dir(int i, char *path) @@ -118,6 +227,7 @@ static int fsck_dir(int i, char *path) while ((de = readdir(dir)) != NULL) { char name[100]; + unsigned char sha1[20]; int len = strlen(de->d_name); switch (len) { @@ -131,8 +241,10 @@ static int fsck_dir(int i, char *path) case 38: sprintf(name, "%02x", i); memcpy(name+2, de->d_name, len+1); - if (!fsck_name(name)) - continue; + if (get_sha1_hex(name, sha1) < 0) + break; + add_sha1_list(sha1, de->d_ino); + continue; } fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name); } @@ -170,6 +282,7 @@ int main(int argc, char **argv) sprintf(dir, "%s/%02x", sha1_dir, i); fsck_dir(i, dir); } + fsck_sha1_list(); heads = 0; for (i = 1; i < argc; i++) { @@ -122,11 +122,6 @@ int parse_tree(struct tree *item) entry->executable = mode & S_IXUSR; entry->next = NULL; - /* Warn about trees that don't do the recursive thing.. */ - if (strchr(path, '/')) { - item->has_full_path = 1; - } - bufptr += len + 20; size -= len + 20; @@ -18,7 +18,6 @@ struct tree_entry_list { struct tree { struct object object; - unsigned has_full_path : 1; struct tree_entry_list *entries; }; |
