Raw
1 #define DISABLE_SIGN_COMPARE_WARNINGS
2
3 #include "git-compat-util.h"
4 #include "gettext.h"
5 #include "strbuf.h"
6 #include "hex.h"
7 #include "repository.h"
8 #include "hash.h"
9 #include "object.h"
10 #include "loose.h"
11 #include "commit.h"
12 #include "gpg-interface.h"
13 #include "object-file-convert.h"
14
15 int repo_oid_to_algop(struct repository *repo, const struct object_id *srcoid,
16 const struct git_hash_algo *to, struct object_id *dest)
17 {
18 /*
19 * If the source algorithm is not set, then we're using the
20 * default hash algorithm for that object.
21 */
22 const struct git_hash_algo *from =
23 srcoid->algo ? &hash_algos[srcoid->algo] : repo->hash_algo;
24 struct object_id temp;
25 const struct object_id *src = srcoid;
26
27 if (!srcoid->algo) {
28 oidcpy(&temp, srcoid);
29 temp.algo = hash_algo_by_ptr(repo->hash_algo);
30 src = &temp;
31 }
32
33 if (from == to || !to) {
34 if (src != dest)
35 oidcpy(dest, src);
36 return 0;
37 }
38 if (repo_loose_object_map_oid(repo, src, to, dest)) {
39 /*
40 * We may have loaded the object map at repo initialization but
41 * another process (perhaps upstream of a pipe from us) may have
42 * written a new object into the map. If the object is missing,
43 * let's reload the map to see if the object has appeared.
44 */
45 repo_read_loose_object_map(repo);
46 if (repo_loose_object_map_oid(repo, src, to, dest))
47 return -1;
48 }
49 return 0;
50 }
51
52 static int decode_tree_entry_raw(struct object_id *oid, const char **path,
53 size_t *len, const struct git_hash_algo *algo,
54 const char *buf, unsigned long size)
55 {
56 uint16_t mode;
57 const unsigned hashsz = algo->rawsz;
58
59 if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
60 return -1;
61 }
62
63 *path = parse_mode(buf, &mode);
64 if (!*path || !**path)
65 return -1;
66 *len = strlen(*path) + 1;
67
68 oidread(oid, (const unsigned char *)*path + *len, algo);
69 return 0;
70 }
71
72 static int convert_tree_object(struct repository *repo,
73 struct strbuf *out,
74 const struct git_hash_algo *from,
75 const struct git_hash_algo *to,
76 const char *buffer, size_t size)
77 {
78 const char *p = buffer, *end = buffer + size;
79
80 while (p < end) {
81 struct object_id entry_oid, mapped_oid;
82 const char *path = NULL;
83 size_t pathlen;
84
85 if (decode_tree_entry_raw(&entry_oid, &path, &pathlen, from, p,
86 end - p))
87 return error(_("failed to decode tree entry"));
88 if (repo_oid_to_algop(repo, &entry_oid, to, &mapped_oid))
89 return error(_("failed to map tree entry for %s"), oid_to_hex(&entry_oid));
90 strbuf_add(out, p, path - p);
91 strbuf_add(out, path, pathlen);
92 strbuf_add(out, mapped_oid.hash, to->rawsz);
93 p = path + pathlen + from->rawsz;
94 }
95 return 0;
96 }
97
98 static int convert_tag_object(struct repository *repo,
99 struct strbuf *out,
100 const struct git_hash_algo *from,
101 const struct git_hash_algo *to,
102 const char *buffer, size_t size)
103 {
104 struct strbuf payload = STRBUF_INIT, oursig = STRBUF_INIT, othersig = STRBUF_INIT;
105 const int entry_len = from->hexsz + 7;
106 size_t payload_size;
107 struct object_id oid, mapped_oid;
108 const char *p;
109
110 /* Consume the object line */
111 if ((entry_len >= size) ||
112 memcmp(buffer, "object ", 7) || buffer[entry_len] != '\n')
113 return error("bogus tag object");
114 if (parse_oid_hex_algop(buffer + 7, &oid, &p, from) < 0)
115 return error("bad tag object ID");
116 if (repo_oid_to_algop(repo, &oid, to, &mapped_oid))
117 return error("unable to map tree %s in tag object",
118 oid_to_hex(&oid));
119 size -= ((p + 1) - buffer);
120 buffer = p + 1;
121
122 /* Is there a signature for our algorithm? */
123 payload_size = parse_signed_buffer(buffer, size);
124 if (payload_size != size) {
125 /* Yes, there is. */
126 strbuf_add(&oursig, buffer + payload_size, size - payload_size);
127 }
128
129 /* Now, is there a signature for the other algorithm? */
130 parse_buffer_signed_by_header(buffer, payload_size, &payload, &othersig, to);
131 /*
132 * Our payload is now in payload and we may have up to two signatrures
133 * in oursig and othersig.
134 */
135
136 /* Add some slop for longer signature header in the new algorithm. */
137 strbuf_grow(out, (7 + to->hexsz + 1) + size + 7);
138 strbuf_addf(out, "object %s\n", oid_to_hex(&mapped_oid));
139 strbuf_addbuf(out, &payload);
140 if (oursig.len)
141 add_header_signature(out, &oursig, from);
142 strbuf_addbuf(out, &othersig);
143
144 strbuf_release(&payload);
145 strbuf_release(&othersig);
146 strbuf_release(&oursig);
147 return 0;
148 }
149
150 static int convert_commit_object(struct repository *repo,
151 struct strbuf *out,
152 const struct git_hash_algo *from,
153 const struct git_hash_algo *to,
154 const char *buffer, size_t size)
155 {
156 const char *tail = buffer;
157 const char *bufptr = buffer;
158 const int tree_entry_len = from->hexsz + 5;
159 const int parent_entry_len = from->hexsz + 7;
160 struct object_id oid, mapped_oid;
161 const char *p, *eol;
162
163 tail += size;
164
165 while ((bufptr < tail) && (*bufptr != '\n')) {
166 eol = memchr(bufptr, '\n', tail - bufptr);
167 if (!eol)
168 return error(_("bad %s in commit"), "line");
169
170 if (((bufptr + 5) < eol) && !memcmp(bufptr, "tree ", 5))
171 {
172 if (((bufptr + tree_entry_len) != eol) ||
173 parse_oid_hex_algop(bufptr + 5, &oid, &p, from) ||
174 (p != eol))
175 return error(_("bad %s in commit"), "tree");
176
177 if (repo_oid_to_algop(repo, &oid, to, &mapped_oid))
178 return error(_("unable to map %s %s in commit object"),
179 "tree", oid_to_hex(&oid));
180 strbuf_addf(out, "tree %s\n", oid_to_hex(&mapped_oid));
181 }
182 else if (((bufptr + 7) < eol) && !memcmp(bufptr, "parent ", 7))
183 {
184 if (((bufptr + parent_entry_len) != eol) ||
185 parse_oid_hex_algop(bufptr + 7, &oid, &p, from) ||
186 (p != eol))
187 return error(_("bad %s in commit"), "parent");
188
189 if (repo_oid_to_algop(repo, &oid, to, &mapped_oid))
190 return error(_("unable to map %s %s in commit object"),
191 "parent", oid_to_hex(&oid));
192
193 strbuf_addf(out, "parent %s\n", oid_to_hex(&mapped_oid));
194 }
195 else if (((bufptr + 9) < eol) && !memcmp(bufptr, "mergetag ", 9))
196 {
197 struct strbuf tag = STRBUF_INIT, new_tag = STRBUF_INIT;
198
199 /* Recover the tag object from the mergetag */
200 strbuf_add(&tag, bufptr + 9, (eol - (bufptr + 9)) + 1);
201
202 bufptr = eol + 1;
203 while ((bufptr < tail) && (*bufptr == ' ')) {
204 eol = memchr(bufptr, '\n', tail - bufptr);
205 if (!eol) {
206 strbuf_release(&tag);
207 return error(_("bad %s in commit"), "mergetag continuation");
208 }
209 strbuf_add(&tag, bufptr + 1, (eol - (bufptr + 1)) + 1);
210 bufptr = eol + 1;
211 }
212
213 /* Compute the new tag object */
214 if (convert_tag_object(repo, &new_tag, from, to, tag.buf, tag.len)) {
215 strbuf_release(&tag);
216 strbuf_release(&new_tag);
217 return -1;
218 }
219
220 /* Write the new mergetag */
221 strbuf_addstr(out, "mergetag");
222 strbuf_add_lines(out, " ", new_tag.buf, new_tag.len);
223 strbuf_release(&tag);
224 strbuf_release(&new_tag);
225 }
226 else if (((bufptr + 7) < tail) && !memcmp(bufptr, "author ", 7))
227 strbuf_add(out, bufptr, (eol - bufptr) + 1);
228 else if (((bufptr + 10) < tail) && !memcmp(bufptr, "committer ", 10))
229 strbuf_add(out, bufptr, (eol - bufptr) + 1);
230 else if (((bufptr + 9) < tail) && !memcmp(bufptr, "encoding ", 9))
231 strbuf_add(out, bufptr, (eol - bufptr) + 1);
232 else if (((bufptr + 6) < tail) && !memcmp(bufptr, "gpgsig", 6))
233 strbuf_add(out, bufptr, (eol - bufptr) + 1);
234 else {
235 /* Unknown line fail it might embed an oid */
236 return -1;
237 }
238 /* Consume any trailing continuation lines */
239 bufptr = eol + 1;
240 while ((bufptr < tail) && (*bufptr == ' ')) {
241 eol = memchr(bufptr, '\n', tail - bufptr);
242 if (!eol)
243 return error(_("bad %s in commit"), "continuation");
244 strbuf_add(out, bufptr, (eol - bufptr) + 1);
245 bufptr = eol + 1;
246 }
247 }
248 if (bufptr < tail)
249 strbuf_add(out, bufptr, tail - bufptr);
250 return 0;
251 }
252
253 int convert_object_file(struct repository *repo,
254 struct strbuf *outbuf,
255 const struct git_hash_algo *from,
256 const struct git_hash_algo *to,
257 const void *buf, size_t len,
258 enum object_type type,
259 int gentle)
260 {
261 int ret;
262
263 /* Don't call this function when no conversion is necessary */
264 if ((from == to) || (type == OBJ_BLOB))
265 BUG("Refusing noop object file conversion");
266
267 switch (type) {
268 case OBJ_COMMIT:
269 ret = convert_commit_object(repo, outbuf, from, to, buf, len);
270 break;
271 case OBJ_TREE:
272 ret = convert_tree_object(repo, outbuf, from, to, buf, len);
273 break;
274 case OBJ_TAG:
275 ret = convert_tag_object(repo, outbuf, from, to, buf, len);
276 break;
277 default:
278 /* Not implemented yet, so fail. */
279 ret = -1;
280 break;
281 }
282 if (!ret)
283 return 0;
284 if (gentle) {
285 strbuf_release(outbuf);
286 return ret;
287 }
288 die(_("Failed to convert object from %s to %s"),
289 from->name, to->name);
290 }