|
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