Git fork
at reftables-rust 283 lines 8.4 kB view 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 "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}