git-mktag.con commit diff-cache: handle modified new files correctly (c9cddab)
   1#include "cache.h"
   2
   3/*
   4 * A signature file has a very simple fixed format: three lines
   5 * of "object <sha1>" + "type <typename>" + "tag <tagname>",
   6 * followed by some free-form signature that git itself doesn't
   7 * care about, but that can be verified with gpg or similar.
   8 *
   9 * The first three lines are guaranteed to be at least 63 bytes:
  10 * "object <sha1>\n" is 48 bytes, "type tag\n" at 9 bytes is the
  11 * shortest possible type-line, and "tag .\n" at 6 bytes is the
  12 * shortest single-character-tag line. 
  13 *
  14 * We also artificially limit the size of the full object to 8kB.
  15 * Just because I'm a lazy bastard, and if you can't fit a signature
  16 * in that size, you're doing something wrong.
  17 */
  18
  19// Some random size
  20#define MAXSIZE (8192)
  21
  22/*
  23 * We refuse to tag something we can't verify. Just because.
  24 */
  25static int verify_object(unsigned char *sha1, const char *expected_type)
  26{
  27        int ret = -1;
  28        unsigned long mapsize;
  29        void *map = map_sha1_file(sha1, &mapsize);
  30
  31        if (map) {
  32                char type[100];
  33                unsigned long size;
  34                void *buffer = unpack_sha1_file(map, mapsize, type, &size);
  35
  36                if (buffer) {
  37                        if (!strcmp(type, expected_type))
  38                                ret = check_sha1_signature(sha1, buffer, size, type);
  39                        free(buffer);
  40                }
  41                munmap(map, mapsize);
  42        }
  43        return ret;
  44}
  45
  46static int verify_tag(char *buffer, unsigned long size)
  47{
  48        int typelen;
  49        char type[20];
  50        unsigned char sha1[20];
  51        const char *object, *type_line, *tag_line;
  52
  53        if (size < 64 || size > MAXSIZE-1)
  54                return -1;
  55        buffer[size] = 0;
  56
  57        /* Verify object line */
  58        object = buffer;
  59        if (memcmp(object, "object ", 7))
  60                return -1;
  61        if (get_sha1_hex(object + 7, sha1))
  62                return -1;
  63
  64        /* Verify type line */
  65        type_line = object + 48;
  66        if (memcmp(type_line - 1, "\ntype ", 6))
  67                return -1;
  68
  69        /* Verify tag-line */
  70        tag_line = strchr(type_line, '\n');
  71        if (!tag_line)
  72                return -1;
  73        tag_line++;
  74        if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n')
  75                return -1;
  76
  77        /* Get the actual type */
  78        typelen = tag_line - type_line - strlen("type \n");
  79        if (typelen >= sizeof(type))
  80                return -1;
  81        memcpy(type, type_line+5, typelen);
  82        type[typelen] = 0;
  83
  84        /* Verify that the object matches */
  85        if (get_sha1_hex(object + 7, sha1))
  86                return -1;
  87        if (verify_object(sha1, type))
  88                return -1;
  89
  90        /* Verify the tag-name: we don't allow control characters or spaces in it */
  91        tag_line += 4;
  92        for (;;) {
  93                unsigned char c = *tag_line++;
  94                if (c == '\n')
  95                        break;
  96                if (c > ' ')
  97                        continue;
  98                return -1;
  99        }
 100
 101        /* The actual stuff afterwards we don't care about.. */
 102        return 0;
 103}
 104
 105int main(int argc, char **argv)
 106{
 107        unsigned long size;
 108        char buffer[MAXSIZE];
 109        unsigned char result_sha1[20];
 110
 111        if (argc != 1)
 112                usage("cat <signaturefile> | git-mktag");
 113
 114        // Read the signature
 115        size = read(0, buffer, MAXSIZE);
 116
 117        // Verify it for some basic sanity: it needs to start with "object <sha1>\ntag "
 118        if (verify_tag(buffer, size) < 0)
 119                die("invalid tag signature file");
 120
 121        if (write_sha1_file(buffer, size, "tag", result_sha1) < 0)
 122                die("unable to write tag file");
 123        printf("%s\n", sha1_to_hex(result_sha1));
 124        return 0;
 125}