|
1 | 1 | #include "builtin.h"
|
| 2 | +#include "parse-options.h" |
2 | 3 | #include "tag.h"
|
3 | 4 | #include "replace-object.h"
|
4 | 5 | #include "object-store.h"
|
| 6 | +#include "fsck.h" |
| 7 | +#include "config.h" |
5 | 8 |
|
6 |
| -/* |
7 |
| - * A signature file has a very simple fixed format: four lines |
8 |
| - * of "object <sha1>" + "type <typename>" + "tag <tagname>" + |
9 |
| - * "tagger <committer>", followed by a blank line, a free-form tag |
10 |
| - * message and a signature block that git itself doesn't care about, |
11 |
| - * but that can be verified with gpg or similar. |
12 |
| - * |
13 |
| - * The first four lines are guaranteed to be at least 83 bytes: |
14 |
| - * "object <sha1>\n" is 48 bytes, "type tag\n" at 9 bytes is the |
15 |
| - * shortest possible type-line, "tag .\n" at 6 bytes is the shortest |
16 |
| - * single-character-tag line, and "tagger . <> 0 +0000\n" at 20 bytes is |
17 |
| - * the shortest possible tagger-line. |
18 |
| - */ |
19 |
| - |
20 |
| -/* |
21 |
| - * We refuse to tag something we can't verify. Just because. |
22 |
| - */ |
23 |
| -static int verify_object(const struct object_id *oid, const char *expected_type) |
| 9 | +static char const * const builtin_mktag_usage[] = { |
| 10 | + N_("git mktag"), |
| 11 | + NULL |
| 12 | +}; |
| 13 | +static int option_strict = 1; |
| 14 | + |
| 15 | +static struct fsck_options fsck_options = FSCK_OPTIONS_STRICT; |
| 16 | + |
| 17 | +static int mktag_config(const char *var, const char *value, void *cb) |
24 | 18 | {
|
25 |
| - int ret = -1; |
26 |
| - enum object_type type; |
27 |
| - unsigned long size; |
28 |
| - void *buffer = read_object_file(oid, &type, &size); |
29 |
| - const struct object_id *repl = lookup_replace_object(the_repository, oid); |
30 |
| - |
31 |
| - if (buffer) { |
32 |
| - if (type == type_from_string(expected_type)) { |
33 |
| - ret = check_object_signature(the_repository, repl, |
34 |
| - buffer, size, |
35 |
| - expected_type); |
| 19 | + return fsck_config_internal(var, value, cb, &fsck_options); |
| 20 | +} |
| 21 | + |
| 22 | +static int mktag_fsck_error_func(struct fsck_options *o, |
| 23 | + const struct object_id *oid, |
| 24 | + enum object_type object_type, |
| 25 | + int msg_type, const char *message) |
| 26 | +{ |
| 27 | + switch (msg_type) { |
| 28 | + case FSCK_WARN: |
| 29 | + if (!option_strict) { |
| 30 | + fprintf_ln(stderr, _("warning: tag input does not pass fsck: %s"), message); |
| 31 | + return 0; |
| 32 | + |
36 | 33 | }
|
37 |
| - free(buffer); |
| 34 | + /* fallthrough */ |
| 35 | + case FSCK_ERROR: |
| 36 | + /* |
| 37 | + * We treat both warnings and errors as errors, things |
| 38 | + * like missing "tagger" lines are "only" warnings |
| 39 | + * under fsck, we've always considered them an error. |
| 40 | + */ |
| 41 | + fprintf_ln(stderr, _("error: tag input does not pass fsck: %s"), message); |
| 42 | + return 1; |
| 43 | + default: |
| 44 | + BUG(_("%d (FSCK_IGNORE?) should never trigger this callback"), |
| 45 | + msg_type); |
38 | 46 | }
|
39 |
| - return ret; |
40 | 47 | }
|
41 | 48 |
|
42 |
| -static int verify_tag(char *buffer, unsigned long size) |
| 49 | +static int verify_object_in_tag(struct object_id *tagged_oid, int *tagged_type) |
43 | 50 | {
|
44 |
| - int typelen; |
45 |
| - char type[20]; |
46 |
| - struct object_id oid; |
47 |
| - const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p; |
48 |
| - size_t len; |
49 |
| - |
50 |
| - if (size < 84) |
51 |
| - return error("wanna fool me ? you obviously got the size wrong !"); |
52 |
| - |
53 |
| - buffer[size] = 0; |
54 |
| - |
55 |
| - /* Verify object line */ |
56 |
| - object = buffer; |
57 |
| - if (memcmp(object, "object ", 7)) |
58 |
| - return error("char%d: does not start with \"object \"", 0); |
59 |
| - |
60 |
| - if (parse_oid_hex(object + 7, &oid, &p)) |
61 |
| - return error("char%d: could not get SHA1 hash", 7); |
62 |
| - |
63 |
| - /* Verify type line */ |
64 |
| - type_line = p + 1; |
65 |
| - if (memcmp(type_line - 1, "\ntype ", 6)) |
66 |
| - return error("char%d: could not find \"\\ntype \"", 47); |
67 |
| - |
68 |
| - /* Verify tag-line */ |
69 |
| - tag_line = strchr(type_line, '\n'); |
70 |
| - if (!tag_line) |
71 |
| - return error("char%"PRIuMAX": could not find next \"\\n\"", |
72 |
| - (uintmax_t) (type_line - buffer)); |
73 |
| - tag_line++; |
74 |
| - if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n') |
75 |
| - return error("char%"PRIuMAX": no \"tag \" found", |
76 |
| - (uintmax_t) (tag_line - buffer)); |
77 |
| - |
78 |
| - /* Get the actual type */ |
79 |
| - typelen = tag_line - type_line - strlen("type \n"); |
80 |
| - if (typelen >= sizeof(type)) |
81 |
| - return error("char%"PRIuMAX": type too long", |
82 |
| - (uintmax_t) (type_line+5 - buffer)); |
83 |
| - |
84 |
| - memcpy(type, type_line+5, typelen); |
85 |
| - type[typelen] = 0; |
86 |
| - |
87 |
| - /* Verify that the object matches */ |
88 |
| - if (verify_object(&oid, type)) |
89 |
| - return error("char%d: could not verify object %s", 7, oid_to_hex(&oid)); |
90 |
| - |
91 |
| - /* Verify the tag-name: we don't allow control characters or spaces in it */ |
92 |
| - tag_line += 4; |
93 |
| - for (;;) { |
94 |
| - unsigned char c = *tag_line++; |
95 |
| - if (c == '\n') |
96 |
| - break; |
97 |
| - if (c > ' ') |
98 |
| - continue; |
99 |
| - return error("char%"PRIuMAX": could not verify tag name", |
100 |
| - (uintmax_t) (tag_line - buffer)); |
101 |
| - } |
| 51 | + int ret; |
| 52 | + enum object_type type; |
| 53 | + unsigned long size; |
| 54 | + void *buffer; |
| 55 | + const struct object_id *repl; |
| 56 | + |
| 57 | + buffer = read_object_file(tagged_oid, &type, &size); |
| 58 | + if (!buffer) |
| 59 | + die(_("could not read tagged object '%s'"), |
| 60 | + oid_to_hex(tagged_oid)); |
| 61 | + if (type != *tagged_type) |
| 62 | + die(_("object '%s' tagged as '%s', but is a '%s' type"), |
| 63 | + oid_to_hex(tagged_oid), |
| 64 | + type_name(*tagged_type), type_name(type)); |
| 65 | + |
| 66 | + repl = lookup_replace_object(the_repository, tagged_oid); |
| 67 | + ret = check_object_signature(the_repository, repl, |
| 68 | + buffer, size, type_name(*tagged_type)); |
| 69 | + free(buffer); |
102 | 70 |
|
103 |
| - /* Verify the tagger line */ |
104 |
| - tagger_line = tag_line; |
105 |
| - |
106 |
| - if (memcmp(tagger_line, "tagger ", 7)) |
107 |
| - return error("char%"PRIuMAX": could not find \"tagger \"", |
108 |
| - (uintmax_t) (tagger_line - buffer)); |
109 |
| - |
110 |
| - /* |
111 |
| - * Check for correct form for name and email |
112 |
| - * i.e. " <" followed by "> " on _this_ line |
113 |
| - * No angle brackets within the name or email address fields. |
114 |
| - * No spaces within the email address field. |
115 |
| - */ |
116 |
| - tagger_line += 7; |
117 |
| - if (!(lb = strstr(tagger_line, " <")) || !(rb = strstr(lb+2, "> ")) || |
118 |
| - strpbrk(tagger_line, "<>\n") != lb+1 || |
119 |
| - strpbrk(lb+2, "><\n ") != rb) |
120 |
| - return error("char%"PRIuMAX": malformed tagger field", |
121 |
| - (uintmax_t) (tagger_line - buffer)); |
122 |
| - |
123 |
| - /* Check for author name, at least one character, space is acceptable */ |
124 |
| - if (lb == tagger_line) |
125 |
| - return error("char%"PRIuMAX": missing tagger name", |
126 |
| - (uintmax_t) (tagger_line - buffer)); |
127 |
| - |
128 |
| - /* timestamp, 1 or more digits followed by space */ |
129 |
| - tagger_line = rb + 2; |
130 |
| - if (!(len = strspn(tagger_line, "0123456789"))) |
131 |
| - return error("char%"PRIuMAX": missing tag timestamp", |
132 |
| - (uintmax_t) (tagger_line - buffer)); |
133 |
| - tagger_line += len; |
134 |
| - if (*tagger_line != ' ') |
135 |
| - return error("char%"PRIuMAX": malformed tag timestamp", |
136 |
| - (uintmax_t) (tagger_line - buffer)); |
137 |
| - tagger_line++; |
138 |
| - |
139 |
| - /* timezone, 5 digits [+-]hhmm, max. 1400 */ |
140 |
| - if (!((tagger_line[0] == '+' || tagger_line[0] == '-') && |
141 |
| - strspn(tagger_line+1, "0123456789") == 4 && |
142 |
| - tagger_line[5] == '\n' && atoi(tagger_line+1) <= 1400)) |
143 |
| - return error("char%"PRIuMAX": malformed tag timezone", |
144 |
| - (uintmax_t) (tagger_line - buffer)); |
145 |
| - tagger_line += 6; |
146 |
| - |
147 |
| - /* Verify the blank line separating the header from the body */ |
148 |
| - if (*tagger_line != '\n') |
149 |
| - return error("char%"PRIuMAX": trailing garbage in tag header", |
150 |
| - (uintmax_t) (tagger_line - buffer)); |
151 |
| - |
152 |
| - /* The actual stuff afterwards we don't care about.. */ |
153 |
| - return 0; |
| 71 | + return ret; |
154 | 72 | }
|
155 | 73 |
|
156 | 74 | int cmd_mktag(int argc, const char **argv, const char *prefix)
|
157 | 75 | {
|
| 76 | + static struct option builtin_mktag_options[] = { |
| 77 | + OPT_BOOL(0, "strict", &option_strict, |
| 78 | + N_("enable more strict checking")), |
| 79 | + OPT_END(), |
| 80 | + }; |
158 | 81 | struct strbuf buf = STRBUF_INIT;
|
| 82 | + struct object_id tagged_oid; |
| 83 | + int tagged_type; |
159 | 84 | struct object_id result;
|
160 | 85 |
|
161 |
| - if (argc != 1) |
162 |
| - usage("git mktag"); |
| 86 | + argc = parse_options(argc, argv, NULL, |
| 87 | + builtin_mktag_options, |
| 88 | + builtin_mktag_usage, 0); |
163 | 89 |
|
164 |
| - if (strbuf_read(&buf, 0, 4096) < 0) { |
165 |
| - die_errno("could not read from stdin"); |
166 |
| - } |
| 90 | + if (strbuf_read(&buf, 0, 0) < 0) |
| 91 | + die_errno(_("could not read from stdin")); |
| 92 | + |
| 93 | + fsck_options.error_func = mktag_fsck_error_func; |
| 94 | + fsck_set_msg_type(&fsck_options, "extraheaderentry", "warn"); |
| 95 | + /* config might set fsck.extraHeaderEntry=* again */ |
| 96 | + git_config(mktag_config, NULL); |
| 97 | + if (fsck_tag_standalone(NULL, buf.buf, buf.len, &fsck_options, |
| 98 | + &tagged_oid, &tagged_type)) |
| 99 | + die(_("tag on stdin did not pass our strict fsck check")); |
167 | 100 |
|
168 |
| - /* Verify it for some basic sanity: it needs to start with |
169 |
| - "object <sha1>\ntype\ntagger " */ |
170 |
| - if (verify_tag(buf.buf, buf.len) < 0) |
171 |
| - die("invalid tag signature file"); |
| 101 | + if (verify_object_in_tag(&tagged_oid, &tagged_type)) |
| 102 | + die(_("tag on stdin did not refer to a valid object")); |
172 | 103 |
|
173 | 104 | if (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)
|
174 |
| - die("unable to write tag file"); |
| 105 | + die(_("unable to write tag file")); |
175 | 106 |
|
176 | 107 | strbuf_release(&buf);
|
177 |
| - printf("%s\n", oid_to_hex(&result)); |
| 108 | + puts(oid_to_hex(&result)); |
178 | 109 | return 0;
|
179 | 110 | }
|
0 commit comments