Skip to content

Commit ec4465a

Browse files
author
Linus Torvalds
committed
Add "tag" objects that can be used to sign other objects.
You use "git-mktag" to create them, and fsck-cache knows how to parse them.
1 parent f5b913c commit ec4465a

File tree

3 files changed

+165
-1
lines changed

3 files changed

+165
-1
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ AR=ar
1616
PROG= update-cache show-diff init-db write-tree read-tree commit-tree \
1717
cat-file fsck-cache checkout-cache diff-tree rev-tree show-files \
1818
check-files ls-tree merge-base merge-cache unpack-file git-export \
19-
diff-cache convert-cache http-pull rpush rpull rev-list
19+
diff-cache convert-cache http-pull rpush rpull rev-list git-mktag
2020

2121
all: $(PROG)
2222

fsck-cache.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,42 @@ static int fsck_blob(unsigned char *sha1, void *data, unsigned long size)
6969
return 0;
7070
}
7171

72+
static int fsck_tag(unsigned char *sha1, void *data, unsigned long size)
73+
{
74+
int typelen, taglen;
75+
unsigned char object[20];
76+
const char *type_line, *tag_line, *sig_line;
77+
78+
if (size < 64)
79+
return -1;
80+
if (memcmp("object ", data, 7) || get_sha1_hex(data + 7, object))
81+
return -1;
82+
83+
type_line = data + 48;
84+
if (memcmp("\ntype ", type_line-1, 6))
85+
return -1;
86+
87+
tag_line = strchr(type_line, '\n');
88+
if (!tag_line || memcmp("tag ", ++tag_line, 4))
89+
return -1;
90+
91+
sig_line = strchr(tag_line, '\n');
92+
if (!sig_line)
93+
return -1;
94+
sig_line++;
95+
96+
typelen = tag_line - type_line - strlen("type \n");
97+
if (typelen >= 20)
98+
return -1;
99+
taglen = sig_line - tag_line - strlen("tag \n");
100+
101+
printf("tagged %.*s %s (%.*s)\n",
102+
typelen, type_line + 5,
103+
sha1_to_hex(object),
104+
taglen, tag_line + 4);
105+
return 0;
106+
}
107+
72108
static int fsck_entry(unsigned char *sha1, char *tag, void *data,
73109
unsigned long size)
74110
{
@@ -81,6 +117,9 @@ static int fsck_entry(unsigned char *sha1, char *tag, void *data,
81117
} else if (!strcmp(tag, "commit")) {
82118
if (fsck_commit(sha1, data, size) < 0)
83119
return -1;
120+
} else if (!strcmp(tag, "tag")) {
121+
if (fsck_tag(sha1, data, size) < 0)
122+
return -1;
84123
} else
85124
return -1;
86125
return 0;

git-mktag.c

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
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+
*/
25+
static 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+
46+
static 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+
105+
int 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+
}

0 commit comments

Comments
 (0)