sha1_file.con commit [PATCH] Teach diff-tree about commit objects (c1fdf2a)
   1/*
   2 * GIT - The information manager from hell
   3 *
   4 * Copyright (C) Linus Torvalds, 2005
   5 *
   6 * This handles basic git sha1 object files - packing, unpacking,
   7 * creation etc.
   8 */
   9#include <stdarg.h>
  10#include "cache.h"
  11
  12const char *sha1_file_directory = NULL;
  13
  14static unsigned hexval(char c)
  15{
  16        if (c >= '0' && c <= '9')
  17                return c - '0';
  18        if (c >= 'a' && c <= 'f')
  19                return c - 'a' + 10;
  20        if (c >= 'A' && c <= 'F')
  21                return c - 'A' + 10;
  22        return ~0;
  23}
  24
  25int get_sha1_hex(const char *hex, unsigned char *sha1)
  26{
  27        int i;
  28        for (i = 0; i < 20; i++) {
  29                unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]);
  30                if (val & ~0xff)
  31                        return -1;
  32                *sha1++ = val;
  33                hex += 2;
  34        }
  35        return 0;
  36}
  37
  38char * sha1_to_hex(const unsigned char *sha1)
  39{
  40        static char buffer[50];
  41        static const char hex[] = "0123456789abcdef";
  42        char *buf = buffer;
  43        int i;
  44
  45        for (i = 0; i < 20; i++) {
  46                unsigned int val = *sha1++;
  47                *buf++ = hex[val >> 4];
  48                *buf++ = hex[val & 0xf];
  49        }
  50        return buffer;
  51}
  52
  53/*
  54 * NOTE! This returns a statically allocated buffer, so you have to be
  55 * careful about using it. Do a "strdup()" if you need to save the
  56 * filename.
  57 */
  58char *sha1_file_name(const unsigned char *sha1)
  59{
  60        int i;
  61        static char *name, *base;
  62
  63        if (!base) {
  64                char *sha1_file_directory = getenv(DB_ENVIRONMENT) ? : DEFAULT_DB_ENVIRONMENT;
  65                int len = strlen(sha1_file_directory);
  66                base = malloc(len + 60);
  67                memcpy(base, sha1_file_directory, len);
  68                memset(base+len, 0, 60);
  69                base[len] = '/';
  70                base[len+3] = '/';
  71                name = base + len + 1;
  72        }
  73        for (i = 0; i < 20; i++) {
  74                static char hex[] = "0123456789abcdef";
  75                unsigned int val = sha1[i];
  76                char *pos = name + i*2 + (i > 0);
  77                *pos++ = hex[val >> 4];
  78                *pos = hex[val & 0xf];
  79        }
  80        return base;
  81}
  82
  83int check_sha1_signature(unsigned char *sha1, void *map, unsigned long size, const char *type)
  84{
  85        char header[100];
  86        unsigned char real_sha1[20];
  87        SHA_CTX c;
  88
  89        SHA1_Init(&c);
  90        SHA1_Update(&c, header, 1+sprintf(header, "%s %lu", type, size));
  91        SHA1_Update(&c, map, size);
  92        SHA1_Final(real_sha1, &c);
  93        return memcmp(sha1, real_sha1, 20) ? -1 : 0;
  94}
  95
  96void *map_sha1_file(const unsigned char *sha1, unsigned long *size)
  97{
  98        char *filename = sha1_file_name(sha1);
  99        int fd = open(filename, O_RDONLY);
 100        struct stat st;
 101        void *map;
 102
 103        if (fd < 0) {
 104                perror(filename);
 105                return NULL;
 106        }
 107        if (fstat(fd, &st) < 0) {
 108                close(fd);
 109                return NULL;
 110        }
 111        map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
 112        close(fd);
 113        if (-1 == (int)(long)map)
 114                return NULL;
 115        *size = st.st_size;
 116        return map;
 117}
 118
 119void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size)
 120{
 121        int ret, bytes;
 122        z_stream stream;
 123        char buffer[8192];
 124        char *buf;
 125
 126        /* Get the data stream */
 127        memset(&stream, 0, sizeof(stream));
 128        stream.next_in = map;
 129        stream.avail_in = mapsize;
 130        stream.next_out = buffer;
 131        stream.avail_out = sizeof(buffer);
 132
 133        inflateInit(&stream);
 134        ret = inflate(&stream, 0);
 135        if (sscanf(buffer, "%10s %lu", type, size) != 2)
 136                return NULL;
 137
 138        bytes = strlen(buffer) + 1;
 139        buf = malloc(*size);
 140        if (!buf)
 141                return NULL;
 142
 143        memcpy(buf, buffer + bytes, stream.total_out - bytes);
 144        bytes = stream.total_out - bytes;
 145        if (bytes < *size && ret == Z_OK) {
 146                stream.next_out = buf + bytes;
 147                stream.avail_out = *size - bytes;
 148                while (inflate(&stream, Z_FINISH) == Z_OK)
 149                        /* nothing */;
 150        }
 151        inflateEnd(&stream);
 152        return buf;
 153}
 154
 155void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size)
 156{
 157        unsigned long mapsize;
 158        void *map, *buf;
 159
 160        map = map_sha1_file(sha1, &mapsize);
 161        if (map) {
 162                buf = unpack_sha1_file(map, mapsize, type, size);
 163                munmap(map, mapsize);
 164                return buf;
 165        }
 166        return NULL;
 167}
 168
 169void *read_tree_with_tree_or_commit_sha1(const unsigned char *sha1,
 170                                         unsigned long *size,
 171                                         unsigned char *tree_sha1_return)
 172{
 173        char type[20];
 174        void *buffer;
 175        unsigned long isize;
 176        int was_commit = 0;
 177        char tree_sha1[20];
 178
 179        buffer = read_sha1_file(sha1, type, &isize);
 180
 181        /* 
 182         * We might have read a commit instead of a tree, in which case
 183         * we parse out the tree_sha1 and attempt to read from there.
 184         * (buffer + 5) is because the tree sha1 is always at offset 5
 185         * in a commit record ("tree ").
 186         */
 187        if (buffer &&
 188            !strcmp(type, "commit") &&
 189            !get_sha1_hex(buffer + 5, tree_sha1)) {
 190                free(buffer);
 191                buffer = read_sha1_file(tree_sha1, type, &isize);
 192                was_commit = 1;
 193        }
 194
 195        /*
 196         * Now do we have something and if so is it a tree?
 197         */
 198        if (!buffer || strcmp(type, "tree")) {
 199                free(buffer);
 200                return;
 201        }
 202
 203        *size = isize;
 204        if (tree_sha1_return)
 205                memcpy(tree_sha1_return, was_commit ? tree_sha1 : sha1, 20);
 206        return buffer;
 207}
 208
 209int write_sha1_file(char *buf, unsigned len, unsigned char *returnsha1)
 210{
 211        int size;
 212        char *compressed;
 213        z_stream stream;
 214        unsigned char sha1[20];
 215        SHA_CTX c;
 216        char *filename;
 217        int fd;
 218
 219        /* Sha1.. */
 220        SHA1_Init(&c);
 221        SHA1_Update(&c, buf, len);
 222        SHA1_Final(sha1, &c);
 223
 224        if (returnsha1)
 225                memcpy(returnsha1, sha1, 20);
 226
 227        filename = sha1_file_name(sha1);
 228        fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
 229        if (fd < 0) {
 230                if (errno != EEXIST)
 231                        return -1;
 232
 233                /*
 234                 * We might do collision checking here, but we'd need to
 235                 * uncompress the old file and check it. Later.
 236                 */
 237                return 0;
 238        }
 239
 240        /* Set it up */
 241        memset(&stream, 0, sizeof(stream));
 242        deflateInit(&stream, Z_BEST_COMPRESSION);
 243        size = deflateBound(&stream, len);
 244        compressed = malloc(size);
 245
 246        /* Compress it */
 247        stream.next_in = buf;
 248        stream.avail_in = len;
 249        stream.next_out = compressed;
 250        stream.avail_out = size;
 251        while (deflate(&stream, Z_FINISH) == Z_OK)
 252                /* nothing */;
 253        deflateEnd(&stream);
 254        size = stream.total_out;
 255
 256        if (write(fd, compressed, size) != size)
 257                die("unable to write file");
 258        close(fd);
 259                
 260        return 0;
 261}
 262
 263static inline int collision_check(char *filename, void *buf, unsigned int size)
 264{
 265#ifdef COLLISION_CHECK
 266        void *map;
 267        int fd = open(filename, O_RDONLY);
 268        struct stat st;
 269        int cmp;
 270
 271        /* Unreadable object, or object went away? Strange. */
 272        if (fd < 0)
 273                return -1;
 274
 275        if (fstat(fd, &st) < 0 || size != st.st_size)
 276                return -1;
 277
 278        map = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
 279        close(fd);
 280        if (map == MAP_FAILED)
 281                return -1;
 282        cmp = memcmp(buf, map, size);
 283        munmap(map, size);
 284        if (cmp)
 285                return -1;
 286#endif
 287        return 0;
 288}
 289
 290int write_sha1_buffer(const unsigned char *sha1, void *buf, unsigned int size)
 291{
 292        char *filename = sha1_file_name(sha1);
 293        int fd;
 294
 295        fd = open(filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
 296        if (fd < 0) {
 297                if (errno != EEXIST)
 298                        return -1;
 299                if (collision_check(filename, buf, size))
 300                        return error("SHA1 collision detected!"
 301                                        " This is bad, bad, BAD!\a\n");
 302                return 0;
 303        }
 304        write(fd, buf, size);
 305        close(fd);
 306        return 0;
 307}