fsck-cache.con commit Make the rev-tree output more regular. This is the last (771364a)
   1#include "cache.h"
   2
   3#include <sys/types.h>
   4#include <dirent.h>
   5
   6struct needs {
   7        unsigned char parent[20];
   8        unsigned char needs[20];
   9        char tag[10];
  10};
  11
  12struct seen {
  13        unsigned char sha1[20];
  14        char tag[10];
  15        unsigned needed;
  16};
  17
  18static struct needs *needs;
  19static struct seen *seen;
  20
  21static int nr_seen, alloc_seen, nr_needs, alloc_needs;
  22
  23/*
  24 * These two functions build up a graph in memory about
  25 * what objects we've referenced, and found, and types..
  26 */
  27static int compare_seen(const void *s1, const void *s2)
  28{
  29        return memcmp(s1, s2, 20);
  30}
  31
  32static int lookup_seen(unsigned char *sha1, char *tag)
  33{
  34        int first = 0, last = nr_seen;
  35
  36        while (last > first) {
  37                int next = (last + first) / 2;
  38                struct seen *s = seen + next;
  39                int cmp = memcmp(sha1, s->sha1, 20);
  40
  41                if (cmp < 0) {
  42                        last = next;
  43                        continue;
  44                }
  45                if (cmp > 0) {
  46                        first = next+1;
  47                        continue;
  48                }
  49                if (strcmp(tag, s->tag))
  50                        break;
  51                s->needed++;
  52                return 1;
  53        }
  54        return 0;
  55}
  56
  57static void check_connectivity(void)
  58{
  59        int i;
  60
  61        /* Sort the "seen" tags for quicker lookup */
  62        qsort(seen, nr_seen, sizeof(struct seen), compare_seen);
  63
  64        /* Look up all the requirements, warn about missing objects.. */
  65        for (i = 0; i < nr_needs; i++) {
  66                struct needs *n = needs + i;
  67                char hex[60];
  68
  69                if (lookup_seen(n->needs, n->tag))
  70                        continue;
  71                strcpy(hex, sha1_to_hex(n->parent));
  72                printf("missing %s: %s referenced by %s\n", n->tag, sha1_to_hex(n->needs), hex);
  73        }
  74
  75        /* Tell the user about things not referenced.. */
  76        for (i = 0; i < nr_seen; i++) {
  77                struct seen *s = seen + i;
  78
  79                if (s->needed)
  80                        continue;
  81                printf("unreferenced %s: %s\n", s->tag, sha1_to_hex(s->sha1));
  82        }
  83}
  84
  85static void mark_needs_sha1(unsigned char *parent, const char * tag, unsigned char *child)
  86{
  87        struct needs *n;
  88
  89        if (nr_needs == alloc_needs) {
  90                alloc_needs = alloc_nr(alloc_needs);
  91                needs = realloc(needs, alloc_needs*sizeof(struct needs));
  92        }
  93        n = needs + nr_needs;
  94        nr_needs++;
  95        memcpy(n->parent, parent, 20);
  96        memcpy(n->needs, child, 20);
  97        strncpy(n->tag, tag, sizeof(n->tag));
  98}
  99
 100static int mark_sha1_seen(unsigned char *sha1, char *tag)
 101{
 102        struct seen *s;
 103
 104        if (nr_seen == alloc_seen) {
 105                alloc_seen = alloc_nr(alloc_seen);
 106                seen = realloc(seen, alloc_seen*sizeof(struct seen));
 107        }
 108        s = seen + nr_seen;
 109        memset(s, 0, sizeof(*s));
 110        nr_seen++;
 111        memcpy(s->sha1, sha1, 20);
 112        strncpy(s->tag, tag, sizeof(s->tag));
 113        
 114        return 0;
 115}
 116
 117static int fsck_tree(unsigned char *sha1, void *data, unsigned long size)
 118{
 119        int warn_old_tree = 1;
 120
 121        while (size) {
 122                int len = 1+strlen(data);
 123                unsigned char *file_sha1 = data + len;
 124                char *path = strchr(data, ' ');
 125                unsigned int mode;
 126                if (size < len + 20 || !path || sscanf(data, "%o", &mode) != 1)
 127                        return -1;
 128
 129                /* Warn about trees that don't do the recursive thing.. */
 130                if (warn_old_tree && strchr(path, '/')) {
 131                        fprintf(stderr, "warning: fsck-cache: tree %s has full pathnames in it\n", sha1_to_hex(sha1));
 132                        warn_old_tree = 0;
 133                }
 134
 135                data += len + 20;
 136                size -= len + 20;
 137                mark_needs_sha1(sha1, S_ISDIR(mode) ? "tree" : "blob", file_sha1);
 138        }
 139        return 0;
 140}
 141
 142static int fsck_commit(unsigned char *sha1, void *data, unsigned long size)
 143{
 144        int parents;
 145        unsigned char tree_sha1[20];
 146        unsigned char parent_sha1[20];
 147
 148        if (memcmp(data, "tree ", 5))
 149                return -1;
 150        if (get_sha1_hex(data + 5, tree_sha1) < 0)
 151                return -1;
 152        mark_needs_sha1(sha1, "tree", tree_sha1);
 153        data += 5 + 40 + 1;     /* "tree " + <hex sha1> + '\n' */
 154        parents = 0;
 155        while (!memcmp(data, "parent ", 7)) {
 156                if (get_sha1_hex(data + 7, parent_sha1) < 0)
 157                        return -1;
 158                mark_needs_sha1(sha1, "commit", parent_sha1);
 159                data += 7 + 40 + 1;     /* "parent " + <hex sha1> + '\n' */
 160                parents++;
 161        }
 162        if (!parents)
 163                printf("root: %s\n", sha1_to_hex(sha1));
 164        return 0;
 165}
 166
 167static int fsck_entry(unsigned char *sha1, char *tag, void *data, unsigned long size)
 168{
 169        if (!strcmp(tag, "blob")) {
 170                /* Nothing to check */;
 171        } else if (!strcmp(tag, "tree")) {
 172                if (fsck_tree(sha1, data, size) < 0)
 173                        return -1;
 174        } else if (!strcmp(tag, "commit")) {
 175                if (fsck_commit(sha1, data, size) < 0)
 176                        return -1;
 177        } else
 178                return -1;
 179        return mark_sha1_seen(sha1, tag);
 180}
 181
 182static int fsck_name(char *hex)
 183{
 184        unsigned char sha1[20];
 185        if (!get_sha1_hex(hex, sha1)) {
 186                unsigned long mapsize;
 187                void *map = map_sha1_file(sha1, &mapsize);
 188                if (map) {
 189                        char type[100];
 190                        unsigned long size;
 191                        void *buffer = NULL;
 192                        if (!check_sha1_signature(sha1, map, mapsize))
 193                                buffer = unpack_sha1_file(map, mapsize, type, &size);
 194                        munmap(map, mapsize);
 195                        if (buffer && !fsck_entry(sha1, type, buffer, size))
 196                                return 0;
 197                }
 198        }
 199        return -1;
 200}
 201
 202static int fsck_dir(int i, char *path)
 203{
 204        DIR *dir = opendir(path);
 205        struct dirent *de;
 206
 207        if (!dir) {
 208                fprintf(stderr, "missing sha1 directory '%s'", path);
 209                return -1;
 210        }
 211
 212        while ((de = readdir(dir)) != NULL) {
 213                char name[100];
 214                int len = strlen(de->d_name);
 215
 216                switch (len) {
 217                case 2:
 218                        if (de->d_name[1] != '.')
 219                                break;
 220                case 1:
 221                        if (de->d_name[0] != '.')
 222                                break;
 223                        continue;
 224                case 38:
 225                        sprintf(name, "%02x", i);
 226                        memcpy(name+2, de->d_name, len+1);
 227                        if (!fsck_name(name))
 228                                continue;
 229                }
 230                fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
 231        }
 232        closedir(dir);
 233        return 0;
 234}
 235
 236int main(int argc, char **argv)
 237{
 238        int i;
 239        char *sha1_dir;
 240
 241        if (argc != 1)
 242                usage("fsck-cache");
 243        sha1_dir = getenv(DB_ENVIRONMENT) ? : DEFAULT_DB_ENVIRONMENT;
 244        for (i = 0; i < 256; i++) {
 245                static char dir[4096];
 246                sprintf(dir, "%s/%02x", sha1_dir, i);
 247                fsck_dir(i, dir);
 248        }
 249        check_connectivity();
 250        return 0;
 251}