git@vger.kernel.org list mirror (unofficial, one of many)
 help / color / mirror / code / Atom feed
blob a1ae80702dee598e7be7c3e0b8fee46cb512bf6f 6368 bytes (raw)
name: builtin/mktag.c 	 # note: path name is non-authoritative(*)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
 
#include "builtin.h"
#include "tag.h"
#include "replace-object.h"
#include "object-store.h"
#include "fsck.h"

/*
 * A signature file has a very simple fixed format: four lines
 * of "object <sha1>" + "type <typename>" + "tag <tagname>" +
 * "tagger <committer>", followed by a blank line, a free-form tag
 * message and a signature block that git itself doesn't care about,
 * but that can be verified with gpg or similar.
 *
 * The first four lines are guaranteed to be at least 83 bytes:
 * "object <sha1>\n" is 48 bytes, "type tag\n" at 9 bytes is the
 * shortest possible type-line, "tag .\n" at 6 bytes is the shortest
 * single-character-tag line, and "tagger . <> 0 +0000\n" at 20 bytes is
 * the shortest possible tagger-line.
 */

/*
 * We refuse to tag something we can't verify. Just because.
 */
static int verify_object(const struct object_id *oid, const char *expected_type)
{
	int ret = -1;
	enum object_type type;
	unsigned long size;
	void *buffer = read_object_file(oid, &type, &size);
	const struct object_id *repl = lookup_replace_object(the_repository, oid);

	if (buffer) {
		if (type == type_from_string(expected_type)) {
			ret = check_object_signature(the_repository, repl,
						     buffer, size,
						     expected_type);
		}
		free(buffer);
	}
	return ret;
}

static int verify_tag(char *buffer, unsigned long size)
{
	int typelen;
	char type[20];
	struct object_id oid;
	const char *object, *type_line, *tag_line, *tagger_line, *lb, *rb, *p;
	size_t len;

	/* verify_tag() will be removed in the next commit */
	return 0;

	if (size < 84)
		return error("wanna fool me ? you obviously got the size wrong !");

	buffer[size] = 0;

	/* Verify object line */
	object = buffer;
	if (memcmp(object, "object ", 7))
		return error("char%d: does not start with \"object \"", 0);

	if (parse_oid_hex(object + 7, &oid, &p))
		return error("char%d: could not get SHA1 hash", 7);

	/* Verify type line */
	type_line = p + 1;
	if (memcmp(type_line - 1, "\ntype ", 6))
		return error("char%d: could not find \"\\ntype \"", 47);

	/* Verify tag-line */
	tag_line = strchr(type_line, '\n');
	if (!tag_line)
		return error("char%"PRIuMAX": could not find next \"\\n\"",
				(uintmax_t) (type_line - buffer));
	tag_line++;
	if (memcmp(tag_line, "tag ", 4) || tag_line[4] == '\n')
		return error("char%"PRIuMAX": no \"tag \" found",
				(uintmax_t) (tag_line - buffer));

	/* Get the actual type */
	typelen = tag_line - type_line - strlen("type \n");
	if (typelen >= sizeof(type))
		return error("char%"PRIuMAX": type too long",
				(uintmax_t) (type_line+5 - buffer));

	memcpy(type, type_line+5, typelen);
	type[typelen] = 0;

	/* Verify that the object matches */
	if (verify_object(&oid, type))
		return error("char%d: could not verify object %s", 7, oid_to_hex(&oid));

	/* Verify the tag-name: we don't allow control characters or spaces in it */
	tag_line += 4;
	for (;;) {
		unsigned char c = *tag_line++;
		if (c == '\n')
			break;
		if (c > ' ')
			continue;
		return error("char%"PRIuMAX": could not verify tag name",
				(uintmax_t) (tag_line - buffer));
	}

	/* Verify the tagger line */
	tagger_line = tag_line;

	if (memcmp(tagger_line, "tagger ", 7))
		return error("char%"PRIuMAX": could not find \"tagger \"",
			(uintmax_t) (tagger_line - buffer));

	/*
	 * Check for correct form for name and email
	 * i.e. " <" followed by "> " on _this_ line
	 * No angle brackets within the name or email address fields.
	 * No spaces within the email address field.
	 */
	tagger_line += 7;
	if (!(lb = strstr(tagger_line, " <")) || !(rb = strstr(lb+2, "> ")) ||
		strpbrk(tagger_line, "<>\n") != lb+1 ||
		strpbrk(lb+2, "><\n ") != rb)
		return error("char%"PRIuMAX": malformed tagger field",
			(uintmax_t) (tagger_line - buffer));

	/* Check for author name, at least one character, space is acceptable */
	if (lb == tagger_line)
		return error("char%"PRIuMAX": missing tagger name",
			(uintmax_t) (tagger_line - buffer));

	/* timestamp, 1 or more digits followed by space */
	tagger_line = rb + 2;
	if (!(len = strspn(tagger_line, "0123456789")))
		return error("char%"PRIuMAX": missing tag timestamp",
			(uintmax_t) (tagger_line - buffer));
	tagger_line += len;
	if (*tagger_line != ' ')
		return error("char%"PRIuMAX": malformed tag timestamp",
			(uintmax_t) (tagger_line - buffer));
	tagger_line++;

	/* timezone, 5 digits [+-]hhmm, max. 1400 */
	if (!((tagger_line[0] == '+' || tagger_line[0] == '-') &&
	      strspn(tagger_line+1, "0123456789") == 4 &&
	      tagger_line[5] == '\n' && atoi(tagger_line+1) <= 1400))
		return error("char%"PRIuMAX": malformed tag timezone",
			(uintmax_t) (tagger_line - buffer));
	tagger_line += 6;

	/* Verify the blank line separating the header from the body */
	if (*tagger_line != '\n')
		return error("char%"PRIuMAX": trailing garbage in tag header",
			(uintmax_t) (tagger_line - buffer));

	/* The actual stuff afterwards we don't care about.. */
	return 0;
}

static int mktag_fsck_error_func(struct fsck_options *o,
				 const struct object_id *oid,
				 enum object_type object_type,
				 int msg_type, const char *message)
{
	switch (msg_type) {
	case FSCK_WARN:
	case FSCK_ERROR:
	case FSCK_EXTRA:
		/*
		 * We treat both warnings and errors as errors, things
		 * like missing "tagger" lines are "only" warnings
		 * under fsck, we've always considered them an error.
		 */
		fprintf_ln(stderr, "error: %s", message);
		return 1;
	default:
		BUG("%d (FSCK_IGNORE?) should never trigger this callback",
		    msg_type);
	}
}

int cmd_mktag(int argc, const char **argv, const char *prefix)
{
	struct object obj;
	struct strbuf buf = STRBUF_INIT;
	struct object_id result;
	struct fsck_options fsck_options = FSCK_OPTIONS_STRICT;

	if (argc != 1)
		usage("git mktag");

	if (strbuf_read(&buf, 0, 0) < 0)
		die_errno("could not read from stdin");

	/* verify_tag() will be removed in the next commit */
	verify_tag("", 0);

	/*
	 * Fake up an object for fsck_object()
	 */
	obj.parsed = 1;
	obj.type = OBJ_TAG;

	fsck_options.extra = 1;
	fsck_options.error_func = mktag_fsck_error_func;
	if (fsck_object(&obj, buf.buf, buf.len, &fsck_options))
		die("tag on stdin did not pass our strict fsck check");

	if (write_object_file(buf.buf, buf.len, tag_type, &result) < 0)
		die("unable to write annotated tag object");

	strbuf_release(&buf);
	printf("%s\n", oid_to_hex(&result));
	return 0;
}

debug log:

solving a1ae80702d ...
found a1ae80702d in https://public-inbox.org/git/20201126012854.399-9-avarab@gmail.com/
found dc354828f7 in https://public-inbox.org/git/20201126222257.5629-5-avarab@gmail.com/ ||
	https://public-inbox.org/git/20201126012854.399-4-avarab@gmail.com/
found 603b55aca0 in https://public-inbox.org/git/20201126222257.5629-4-avarab@gmail.com/ ||
	https://public-inbox.org/git/20201126012854.399-3-avarab@gmail.com/
found ff7ac8e0e5 in https://80x24.org/mirrors/git.git
preparing index
index prepared:
100644 ff7ac8e0e5dbfe77ca16d2304b944a30c14ca408	builtin/mktag.c

applying [1/3] https://public-inbox.org/git/20201126222257.5629-4-avarab@gmail.com/
diff --git a/builtin/mktag.c b/builtin/mktag.c
index ff7ac8e0e5..603b55aca0 100644

Checking patch builtin/mktag.c...
Applied patch builtin/mktag.c cleanly.

skipping https://public-inbox.org/git/20201126012854.399-3-avarab@gmail.com/ for 603b55aca0
index at:
100644 603b55aca0e91d9ed2272cf8ea0ce9a0fdd9dbe5	builtin/mktag.c

applying [2/3] https://public-inbox.org/git/20201126222257.5629-5-avarab@gmail.com/
diff --git a/builtin/mktag.c b/builtin/mktag.c
index 603b55aca0..dc354828f7 100644

Checking patch builtin/mktag.c...
Applied patch builtin/mktag.c cleanly.

skipping https://public-inbox.org/git/20201126012854.399-4-avarab@gmail.com/ for dc354828f7
index at:
100644 dc354828f7d34ac5c66b73479864f254bf1b8157	builtin/mktag.c

applying [3/3] https://public-inbox.org/git/20201126012854.399-9-avarab@gmail.com/
diff --git a/builtin/mktag.c b/builtin/mktag.c
index dc354828f7..a1ae80702d 100644

Checking patch builtin/mktag.c...
Applied patch builtin/mktag.c cleanly.

index at:
100644 a1ae80702dee598e7be7c3e0b8fee46cb512bf6f	builtin/mktag.c

(*) Git path names are given by the tree(s) the blob belongs to.
    Blobs themselves have no identifier aside from the hash of its contents.^

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).