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