| File: | http-walker.c |
| Location: | line 561, column 9 |
| Description: | Access to field 'base' results in a dereference of a null pointer (loaded from field 'alt') |
| 1 | #include "cache.h" | |||||
| 2 | #include "commit.h" | |||||
| 3 | #include "walker.h" | |||||
| 4 | #include "http.h" | |||||
| 5 | #include "list.h" | |||||
| 6 | #include "transport.h" | |||||
| 7 | ||||||
| 8 | struct alt_base { | |||||
| 9 | char *base; | |||||
| 10 | int got_indices; | |||||
| 11 | struct packed_git *packs; | |||||
| 12 | struct alt_base *next; | |||||
| 13 | }; | |||||
| 14 | ||||||
| 15 | enum object_request_state { | |||||
| 16 | WAITING, | |||||
| 17 | ABORTED, | |||||
| 18 | ACTIVE, | |||||
| 19 | COMPLETE | |||||
| 20 | }; | |||||
| 21 | ||||||
| 22 | struct object_request { | |||||
| 23 | struct walker *walker; | |||||
| 24 | unsigned char sha1[20]; | |||||
| 25 | struct alt_base *repo; | |||||
| 26 | enum object_request_state state; | |||||
| 27 | struct http_object_request *req; | |||||
| 28 | struct list_head node; | |||||
| 29 | }; | |||||
| 30 | ||||||
| 31 | struct alternates_request { | |||||
| 32 | struct walker *walker; | |||||
| 33 | const char *base; | |||||
| 34 | struct strbuf *url; | |||||
| 35 | struct strbuf *buffer; | |||||
| 36 | struct active_request_slot *slot; | |||||
| 37 | int http_specific; | |||||
| 38 | }; | |||||
| 39 | ||||||
| 40 | struct walker_data { | |||||
| 41 | const char *url; | |||||
| 42 | int got_alternates; | |||||
| 43 | struct alt_base *alt; | |||||
| 44 | }; | |||||
| 45 | ||||||
| 46 | static LIST_HEAD(object_queue_head)struct list_head object_queue_head = { &(object_queue_head ), &(object_queue_head) }; | |||||
| 47 | ||||||
| 48 | static void fetch_alternates(struct walker *walker, const char *base); | |||||
| 49 | ||||||
| 50 | static void process_object_response(void *callback_data); | |||||
| 51 | ||||||
| 52 | static void start_object_request(struct walker *walker, | |||||
| 53 | struct object_request *obj_req) | |||||
| 54 | { | |||||
| 55 | struct active_request_slot *slot; | |||||
| 56 | struct http_object_request *req; | |||||
| 57 | ||||||
| 58 | req = new_http_object_request(obj_req->repo->base, obj_req->sha1); | |||||
| 59 | if (req == NULL((void*)0)) { | |||||
| 60 | obj_req->state = ABORTED; | |||||
| 61 | return; | |||||
| 62 | } | |||||
| 63 | obj_req->req = req; | |||||
| 64 | ||||||
| 65 | slot = req->slot; | |||||
| 66 | slot->callback_func = process_object_response; | |||||
| 67 | slot->callback_data = obj_req; | |||||
| 68 | ||||||
| 69 | /* Try to get the request started, abort the request on error */ | |||||
| 70 | obj_req->state = ACTIVE; | |||||
| 71 | if (!start_active_slot(slot)) { | |||||
| 72 | obj_req->state = ABORTED; | |||||
| 73 | release_http_object_request(req); | |||||
| 74 | return; | |||||
| 75 | } | |||||
| 76 | } | |||||
| 77 | ||||||
| 78 | static void finish_object_request(struct object_request *obj_req) | |||||
| 79 | { | |||||
| 80 | if (finish_http_object_request(obj_req->req)) | |||||
| 81 | return; | |||||
| 82 | ||||||
| 83 | if (obj_req->req->rename == 0) | |||||
| 84 | walker_say(obj_req->walker, "got %s\n", sha1_to_hex(obj_req->sha1)); | |||||
| 85 | } | |||||
| 86 | ||||||
| 87 | static void process_object_response(void *callback_data) | |||||
| 88 | { | |||||
| 89 | struct object_request *obj_req = | |||||
| 90 | (struct object_request *)callback_data; | |||||
| 91 | struct walker *walker = obj_req->walker; | |||||
| 92 | struct walker_data *data = walker->data; | |||||
| 93 | struct alt_base *alt = data->alt; | |||||
| 94 | ||||||
| 95 | process_http_object_request(obj_req->req); | |||||
| 96 | obj_req->state = COMPLETE; | |||||
| 97 | ||||||
| 98 | /* Use alternates if necessary */ | |||||
| 99 | if (missing_target(obj_req->req)missing__target((obj_req->req)->http_code, (obj_req-> req)->curl_result)) { | |||||
| 100 | fetch_alternates(walker, alt->base); | |||||
| 101 | if (obj_req->repo->next != NULL((void*)0)) { | |||||
| 102 | obj_req->repo = | |||||
| 103 | obj_req->repo->next; | |||||
| 104 | release_http_object_request(obj_req->req); | |||||
| 105 | start_object_request(walker, obj_req); | |||||
| 106 | return; | |||||
| 107 | } | |||||
| 108 | } | |||||
| 109 | ||||||
| 110 | finish_object_request(obj_req); | |||||
| 111 | } | |||||
| 112 | ||||||
| 113 | static void release_object_request(struct object_request *obj_req) | |||||
| 114 | { | |||||
| 115 | if (obj_req->req !=NULL((void*)0) && obj_req->req->localfile != -1) | |||||
| 116 | error("fd leakage in release: %d", obj_req->req->localfile)(error("fd leakage in release: %d", obj_req->req->localfile ), const_error()); | |||||
| 117 | ||||||
| 118 | list_del(&obj_req->node); | |||||
| 119 | free(obj_req); | |||||
| 120 | } | |||||
| 121 | ||||||
| 122 | #ifdef USE_CURL_MULTI | |||||
| 123 | static int fill_active_slot(struct walker *walker) | |||||
| 124 | { | |||||
| 125 | struct object_request *obj_req; | |||||
| 126 | struct list_head *pos, *tmp, *head = &object_queue_head; | |||||
| 127 | ||||||
| 128 | list_for_each_safe(pos, tmp, head)for (pos = (head)->next, tmp = pos->next; pos != (head) ; pos = tmp, tmp = pos->next) { | |||||
| 129 | obj_req = list_entry(pos, struct object_request, node)((struct object_request *) ((char *) (pos) - __builtin_offsetof (struct object_request, node))); | |||||
| 130 | if (obj_req->state == WAITING) { | |||||
| 131 | if (has_sha1_file(obj_req->sha1)) | |||||
| 132 | obj_req->state = COMPLETE; | |||||
| 133 | else { | |||||
| 134 | start_object_request(walker, obj_req); | |||||
| 135 | return 1; | |||||
| 136 | } | |||||
| 137 | } | |||||
| 138 | } | |||||
| 139 | return 0; | |||||
| 140 | } | |||||
| 141 | #endif | |||||
| 142 | ||||||
| 143 | static void prefetch(struct walker *walker, unsigned char *sha1) | |||||
| 144 | { | |||||
| 145 | struct object_request *newreq; | |||||
| 146 | struct walker_data *data = walker->data; | |||||
| 147 | ||||||
| 148 | newreq = xmalloc(sizeof(*newreq)); | |||||
| 149 | newreq->walker = walker; | |||||
| 150 | hashcpy(newreq->sha1, sha1); | |||||
| 151 | newreq->repo = data->alt; | |||||
| 152 | newreq->state = WAITING; | |||||
| 153 | newreq->req = NULL((void*)0); | |||||
| 154 | ||||||
| 155 | http_is_verbose = walker->get_verbosely; | |||||
| 156 | list_add_tail(&newreq->node, &object_queue_head); | |||||
| 157 | ||||||
| 158 | #ifdef USE_CURL_MULTI | |||||
| 159 | fill_active_slots(); | |||||
| 160 | step_active_slots(); | |||||
| 161 | #endif | |||||
| 162 | } | |||||
| 163 | ||||||
| 164 | static int is_alternate_allowed(const char *url) | |||||
| 165 | { | |||||
| 166 | const char *protocols[] = { | |||||
| 167 | "http", "https", "ftp", "ftps" | |||||
| 168 | }; | |||||
| 169 | int i; | |||||
| 170 | ||||||
| 171 | for (i = 0; i < ARRAY_SIZE(protocols)(sizeof(protocols) / sizeof((protocols)[0]) + (sizeof(char [1 - 2*!(!__builtin_types_compatible_p(__typeof__(protocols), __typeof__ (&(protocols)[0])))]) - 1)); i++) { | |||||
| 172 | const char *end; | |||||
| 173 | if (skip_prefix(url, protocols[i], &end) && | |||||
| 174 | starts_with(end, "://")) | |||||
| 175 | break; | |||||
| 176 | } | |||||
| 177 | ||||||
| 178 | if (i >= ARRAY_SIZE(protocols)(sizeof(protocols) / sizeof((protocols)[0]) + (sizeof(char [1 - 2*!(!__builtin_types_compatible_p(__typeof__(protocols), __typeof__ (&(protocols)[0])))]) - 1))) { | |||||
| 179 | warning("ignoring alternate with unknown protocol: %s", url); | |||||
| 180 | return 0; | |||||
| 181 | } | |||||
| 182 | if (!is_transport_allowed(protocols[i], 0)) { | |||||
| 183 | warning("ignoring alternate with restricted protocol: %s", url); | |||||
| 184 | return 0; | |||||
| 185 | } | |||||
| 186 | ||||||
| 187 | return 1; | |||||
| 188 | } | |||||
| 189 | ||||||
| 190 | static void process_alternates_response(void *callback_data) | |||||
| 191 | { | |||||
| 192 | struct alternates_request *alt_req = | |||||
| 193 | (struct alternates_request *)callback_data; | |||||
| 194 | struct walker *walker = alt_req->walker; | |||||
| 195 | struct walker_data *cdata = walker->data; | |||||
| 196 | struct active_request_slot *slot = alt_req->slot; | |||||
| 197 | struct alt_base *tail = cdata->alt; | |||||
| 198 | const char *base = alt_req->base; | |||||
| 199 | const char null_byte = '\0'; | |||||
| 200 | char *data; | |||||
| 201 | int i = 0; | |||||
| 202 | ||||||
| 203 | if (alt_req->http_specific) { | |||||
| 204 | if (slot->curl_result != CURLE_OK || | |||||
| 205 | !alt_req->buffer->len) { | |||||
| 206 | ||||||
| 207 | /* Try reusing the slot to get non-http alternates */ | |||||
| 208 | alt_req->http_specific = 0; | |||||
| 209 | strbuf_reset(alt_req->url)strbuf_setlen(alt_req->url, 0); | |||||
| 210 | strbuf_addf(alt_req->url, "%s/objects/info/alternates", | |||||
| 211 | base); | |||||
| 212 | curl_easy_setopt(slot->curl, CURLOPT_URL,curl_easy_setopt(slot->curl,CURLOPT_URL,alt_req->url-> buf) | |||||
| 213 | alt_req->url->buf)curl_easy_setopt(slot->curl,CURLOPT_URL,alt_req->url-> buf); | |||||
| 214 | active_requests++; | |||||
| 215 | slot->in_use = 1; | |||||
| 216 | if (slot->finished != NULL((void*)0)) | |||||
| 217 | (*slot->finished) = 0; | |||||
| 218 | if (!start_active_slot(slot)) { | |||||
| 219 | cdata->got_alternates = -1; | |||||
| 220 | slot->in_use = 0; | |||||
| 221 | if (slot->finished != NULL((void*)0)) | |||||
| 222 | (*slot->finished) = 1; | |||||
| 223 | } | |||||
| 224 | return; | |||||
| 225 | } | |||||
| 226 | } else if (slot->curl_result != CURLE_OK) { | |||||
| 227 | if (!missing_target(slot)missing__target((slot)->http_code, (slot)->curl_result)) { | |||||
| 228 | cdata->got_alternates = -1; | |||||
| 229 | return; | |||||
| 230 | } | |||||
| 231 | } | |||||
| 232 | ||||||
| 233 | fwrite_buffer((char *)&null_byte, 1, 1, alt_req->buffer); | |||||
| 234 | alt_req->buffer->len--; | |||||
| 235 | data = alt_req->buffer->buf; | |||||
| 236 | ||||||
| 237 | while (i < alt_req->buffer->len) { | |||||
| 238 | int posn = i; | |||||
| 239 | while (posn < alt_req->buffer->len && data[posn] != '\n') | |||||
| 240 | posn++; | |||||
| 241 | if (data[posn] == '\n') { | |||||
| 242 | int okay = 0; | |||||
| 243 | int serverlen = 0; | |||||
| 244 | struct alt_base *newalt; | |||||
| 245 | if (data[i] == '/') { | |||||
| 246 | /* | |||||
| 247 | * This counts | |||||
| 248 | * http://git.host/pub/scm/linux.git/ | |||||
| 249 | * -----------here^ | |||||
| 250 | * so memcpy(dst, base, serverlen) will | |||||
| 251 | * copy up to "...git.host". | |||||
| 252 | */ | |||||
| 253 | const char *colon_ss = strstr(base,"://"); | |||||
| 254 | if (colon_ss) { | |||||
| 255 | serverlen = (strchr(colon_ss + 3, '/') | |||||
| 256 | - base); | |||||
| 257 | okay = 1; | |||||
| 258 | } | |||||
| 259 | } else if (!memcmp(data + i, "../", 3)) { | |||||
| 260 | /* | |||||
| 261 | * Relative URL; chop the corresponding | |||||
| 262 | * number of subpath from base (and ../ | |||||
| 263 | * from data), and concatenate the result. | |||||
| 264 | * | |||||
| 265 | * The code first drops ../ from data, and | |||||
| 266 | * then drops one ../ from data and one path | |||||
| 267 | * from base. IOW, one extra ../ is dropped | |||||
| 268 | * from data than path is dropped from base. | |||||
| 269 | * | |||||
| 270 | * This is not wrong. The alternate in | |||||
| 271 | * http://git.host/pub/scm/linux.git/ | |||||
| 272 | * to borrow from | |||||
| 273 | * http://git.host/pub/scm/linus.git/ | |||||
| 274 | * is ../../linus.git/objects/. You need | |||||
| 275 | * two ../../ to borrow from your direct | |||||
| 276 | * neighbour. | |||||
| 277 | */ | |||||
| 278 | i += 3; | |||||
| 279 | serverlen = strlen(base); | |||||
| 280 | while (i + 2 < posn && | |||||
| 281 | !memcmp(data + i, "../", 3)) { | |||||
| 282 | do { | |||||
| 283 | serverlen--; | |||||
| 284 | } while (serverlen && | |||||
| 285 | base[serverlen - 1] != '/'); | |||||
| 286 | i += 3; | |||||
| 287 | } | |||||
| 288 | /* If the server got removed, give up. */ | |||||
| 289 | okay = strchr(base, ':') - base + 3 < | |||||
| 290 | serverlen; | |||||
| 291 | } else if (alt_req->http_specific) { | |||||
| 292 | char *colon = strchr(data + i, ':'); | |||||
| 293 | char *slash = strchr(data + i, '/'); | |||||
| 294 | if (colon && slash && colon < data + posn && | |||||
| 295 | slash < data + posn && colon < slash) { | |||||
| 296 | okay = 1; | |||||
| 297 | } | |||||
| 298 | } | |||||
| 299 | /* skip "objects\n" at end */ | |||||
| 300 | if (okay) { | |||||
| 301 | struct strbuf target = STRBUF_INIT{ 0, 0, strbuf_slopbuf }; | |||||
| 302 | strbuf_add(&target, base, serverlen); | |||||
| 303 | strbuf_add(&target, data + i, posn - i - 7); | |||||
| 304 | ||||||
| 305 | if (is_alternate_allowed(target.buf)) { | |||||
| 306 | warning("adding alternate object store: %s", | |||||
| 307 | target.buf); | |||||
| 308 | newalt = xmalloc(sizeof(*newalt)); | |||||
| 309 | newalt->next = NULL((void*)0); | |||||
| 310 | newalt->base = strbuf_detach(&target, NULL((void*)0)); | |||||
| 311 | newalt->got_indices = 0; | |||||
| 312 | newalt->packs = NULL((void*)0); | |||||
| 313 | ||||||
| 314 | while (tail->next != NULL((void*)0)) | |||||
| 315 | tail = tail->next; | |||||
| 316 | tail->next = newalt; | |||||
| 317 | } | |||||
| 318 | } | |||||
| 319 | } | |||||
| 320 | i = posn + 1; | |||||
| 321 | } | |||||
| 322 | ||||||
| 323 | cdata->got_alternates = 1; | |||||
| 324 | } | |||||
| 325 | ||||||
| 326 | static void fetch_alternates(struct walker *walker, const char *base) | |||||
| 327 | { | |||||
| 328 | struct strbuf buffer = STRBUF_INIT{ 0, 0, strbuf_slopbuf }; | |||||
| 329 | struct strbuf url = STRBUF_INIT{ 0, 0, strbuf_slopbuf }; | |||||
| 330 | struct active_request_slot *slot; | |||||
| 331 | struct alternates_request alt_req; | |||||
| 332 | struct walker_data *cdata = walker->data; | |||||
| 333 | ||||||
| 334 | if (http_follow_config != HTTP_FOLLOW_ALWAYS) | |||||
| 335 | return; | |||||
| 336 | ||||||
| 337 | /* | |||||
| 338 | * If another request has already started fetching alternates, | |||||
| 339 | * wait for them to arrive and return to processing this request's | |||||
| 340 | * curl message | |||||
| 341 | */ | |||||
| 342 | #ifdef USE_CURL_MULTI | |||||
| 343 | while (cdata->got_alternates == 0) { | |||||
| 344 | step_active_slots(); | |||||
| 345 | } | |||||
| 346 | #endif | |||||
| 347 | ||||||
| 348 | /* Nothing to do if they've already been fetched */ | |||||
| 349 | if (cdata->got_alternates == 1) | |||||
| 350 | return; | |||||
| 351 | ||||||
| 352 | /* Start the fetch */ | |||||
| 353 | cdata->got_alternates = 0; | |||||
| 354 | ||||||
| 355 | if (walker->get_verbosely) | |||||
| 356 | fprintf(stderr__stderrp, "Getting alternates list for %s\n", base); | |||||
| 357 | ||||||
| 358 | strbuf_addf(&url, "%s/objects/info/http-alternates", base); | |||||
| 359 | ||||||
| 360 | /* | |||||
| 361 | * Use a callback to process the result, since another request | |||||
| 362 | * may fail and need to have alternates loaded before continuing | |||||
| 363 | */ | |||||
| 364 | slot = get_active_slot(); | |||||
| 365 | slot->callback_func = process_alternates_response; | |||||
| 366 | alt_req.walker = walker; | |||||
| 367 | slot->callback_data = &alt_req; | |||||
| 368 | ||||||
| 369 | curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer)curl_easy_setopt(slot->curl,CURLOPT_WRITEDATA,&buffer); | |||||
| 370 | curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer)curl_easy_setopt(slot->curl,CURLOPT_WRITEFUNCTION,fwrite_buffer ); | |||||
| 371 | curl_easy_setopt(slot->curl, CURLOPT_URL, url.buf)curl_easy_setopt(slot->curl,CURLOPT_URL,url.buf); | |||||
| 372 | ||||||
| 373 | alt_req.base = base; | |||||
| 374 | alt_req.url = &url; | |||||
| 375 | alt_req.buffer = &buffer; | |||||
| 376 | alt_req.http_specific = 1; | |||||
| 377 | alt_req.slot = slot; | |||||
| 378 | ||||||
| 379 | if (start_active_slot(slot)) | |||||
| 380 | run_active_slot(slot); | |||||
| 381 | else | |||||
| 382 | cdata->got_alternates = -1; | |||||
| 383 | ||||||
| 384 | strbuf_release(&buffer); | |||||
| 385 | strbuf_release(&url); | |||||
| 386 | } | |||||
| 387 | ||||||
| 388 | static int fetch_indices(struct walker *walker, struct alt_base *repo) | |||||
| 389 | { | |||||
| 390 | int ret; | |||||
| 391 | ||||||
| 392 | if (repo->got_indices) | |||||
| 393 | return 0; | |||||
| 394 | ||||||
| 395 | if (walker->get_verbosely) | |||||
| 396 | fprintf(stderr__stderrp, "Getting pack list for %s\n", repo->base); | |||||
| 397 | ||||||
| 398 | switch (http_get_info_packs(repo->base, &repo->packs)) { | |||||
| 399 | case HTTP_OK0: | |||||
| 400 | case HTTP_MISSING_TARGET1: | |||||
| 401 | repo->got_indices = 1; | |||||
| 402 | ret = 0; | |||||
| 403 | break; | |||||
| 404 | default: | |||||
| 405 | repo->got_indices = 0; | |||||
| 406 | ret = -1; | |||||
| 407 | } | |||||
| 408 | ||||||
| 409 | return ret; | |||||
| 410 | } | |||||
| 411 | ||||||
| 412 | static int http_fetch_pack(struct walker *walker, struct alt_base *repo, unsigned char *sha1) | |||||
| 413 | { | |||||
| 414 | struct packed_git *target; | |||||
| 415 | int ret; | |||||
| 416 | struct slot_results results; | |||||
| 417 | struct http_pack_request *preq; | |||||
| 418 | ||||||
| 419 | if (fetch_indices(walker, repo)) | |||||
| 420 | return -1; | |||||
| 421 | target = find_sha1_pack(sha1, repo->packs); | |||||
| 422 | if (!target) | |||||
| 423 | return -1; | |||||
| 424 | ||||||
| 425 | if (walker->get_verbosely) { | |||||
| 426 | fprintf(stderr__stderrp, "Getting pack %s\n", | |||||
| 427 | sha1_to_hex(target->sha1)); | |||||
| 428 | fprintf(stderr__stderrp, " which contains %s\n", | |||||
| 429 | sha1_to_hex(sha1)); | |||||
| 430 | } | |||||
| 431 | ||||||
| 432 | preq = new_http_pack_request(target, repo->base); | |||||
| 433 | if (preq == NULL((void*)0)) | |||||
| 434 | goto abort; | |||||
| 435 | preq->lst = &repo->packs; | |||||
| 436 | preq->slot->results = &results; | |||||
| 437 | ||||||
| 438 | if (start_active_slot(preq->slot)) { | |||||
| 439 | run_active_slot(preq->slot); | |||||
| 440 | if (results.curl_result != CURLE_OK) { | |||||
| 441 | error("Unable to get pack file %s\n%s", preq->url,(error("Unable to get pack file %s\n%s", preq->url, curl_errorstr ), const_error()) | |||||
| 442 | curl_errorstr)(error("Unable to get pack file %s\n%s", preq->url, curl_errorstr ), const_error()); | |||||
| 443 | goto abort; | |||||
| 444 | } | |||||
| 445 | } else { | |||||
| 446 | error("Unable to start request")(error("Unable to start request"), const_error()); | |||||
| 447 | goto abort; | |||||
| 448 | } | |||||
| 449 | ||||||
| 450 | ret = finish_http_pack_request(preq); | |||||
| 451 | release_http_pack_request(preq); | |||||
| 452 | if (ret) | |||||
| 453 | return ret; | |||||
| 454 | ||||||
| 455 | return 0; | |||||
| 456 | ||||||
| 457 | abort: | |||||
| 458 | return -1; | |||||
| 459 | } | |||||
| 460 | ||||||
| 461 | static void abort_object_request(struct object_request *obj_req) | |||||
| 462 | { | |||||
| 463 | release_object_request(obj_req); | |||||
| 464 | } | |||||
| 465 | ||||||
| 466 | static int fetch_object(struct walker *walker, unsigned char *sha1) | |||||
| 467 | { | |||||
| 468 | char *hex = sha1_to_hex(sha1); | |||||
| 469 | int ret = 0; | |||||
| 470 | struct object_request *obj_req = NULL((void*)0); | |||||
| 471 | struct http_object_request *req; | |||||
| 472 | struct list_head *pos, *head = &object_queue_head; | |||||
| 473 | ||||||
| 474 | list_for_each(pos, head)for (pos = (head)->next; pos != (head); pos = pos->next ) { | |||||
| 475 | obj_req = list_entry(pos, struct object_request, node)((struct object_request *) ((char *) (pos) - __builtin_offsetof (struct object_request, node))); | |||||
| 476 | if (!hashcmp(obj_req->sha1, sha1)) | |||||
| 477 | break; | |||||
| 478 | } | |||||
| 479 | if (obj_req == NULL((void*)0)) | |||||
| 480 | return error("Couldn't find request for %s in the queue", hex)(error("Couldn't find request for %s in the queue", hex), const_error ()); | |||||
| 481 | ||||||
| 482 | if (has_sha1_file(obj_req->sha1)) { | |||||
| 483 | if (obj_req->req != NULL((void*)0)) | |||||
| 484 | abort_http_object_request(obj_req->req); | |||||
| 485 | abort_object_request(obj_req); | |||||
| 486 | return 0; | |||||
| 487 | } | |||||
| 488 | ||||||
| 489 | #ifdef USE_CURL_MULTI | |||||
| 490 | while (obj_req->state == WAITING) | |||||
| 491 | step_active_slots(); | |||||
| 492 | #else | |||||
| 493 | start_object_request(walker, obj_req); | |||||
| 494 | #endif | |||||
| 495 | ||||||
| 496 | /* | |||||
| 497 | * obj_req->req might change when fetching alternates in the callback | |||||
| 498 | * process_object_response; therefore, the "shortcut" variable, req, | |||||
| 499 | * is used only after we're done with slots. | |||||
| 500 | */ | |||||
| 501 | while (obj_req->state == ACTIVE) | |||||
| 502 | run_active_slot(obj_req->req->slot); | |||||
| 503 | ||||||
| 504 | req = obj_req->req; | |||||
| 505 | ||||||
| 506 | if (req->localfile != -1) { | |||||
| 507 | close(req->localfile); | |||||
| 508 | req->localfile = -1; | |||||
| 509 | } | |||||
| 510 | ||||||
| 511 | /* | |||||
| 512 | * we turned off CURLOPT_FAILONERROR to avoid losing a | |||||
| 513 | * persistent connection and got CURLE_OK. | |||||
| 514 | */ | |||||
| 515 | if (req->http_code >= 300 && req->curl_result == CURLE_OK && | |||||
| 516 | (starts_with(req->url, "http://") || | |||||
| 517 | starts_with(req->url, "https://"))) { | |||||
| 518 | req->curl_result = CURLE_HTTP_RETURNED_ERROR; | |||||
| 519 | xsnprintf(req->errorstr, sizeof(req->errorstr), | |||||
| 520 | "HTTP request failed"); | |||||
| 521 | } | |||||
| 522 | ||||||
| 523 | if (obj_req->state == ABORTED) { | |||||
| 524 | ret = error("Request for %s aborted", hex)(error("Request for %s aborted", hex), const_error()); | |||||
| 525 | } else if (req->curl_result != CURLE_OK && | |||||
| 526 | req->http_code != 416) { | |||||
| 527 | if (missing_target(req)missing__target((req)->http_code, (req)->curl_result)) | |||||
| 528 | ret = -1; /* Be silent, it is probably in a pack. */ | |||||
| 529 | else | |||||
| 530 | ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",(error("%s (curl_result = %d, http_code = %ld, sha1 = %s)", req ->errorstr, req->curl_result, req->http_code, hex), const_error ()) | |||||
| 531 | req->errorstr, req->curl_result,(error("%s (curl_result = %d, http_code = %ld, sha1 = %s)", req ->errorstr, req->curl_result, req->http_code, hex), const_error ()) | |||||
| 532 | req->http_code, hex)(error("%s (curl_result = %d, http_code = %ld, sha1 = %s)", req ->errorstr, req->curl_result, req->http_code, hex), const_error ()); | |||||
| 533 | } else if (req->zret != Z_STREAM_END1) { | |||||
| 534 | walker->corrupt_object_found++; | |||||
| 535 | ret = error("File %s (%s) corrupt", hex, req->url)(error("File %s (%s) corrupt", hex, req->url), const_error ()); | |||||
| 536 | } else if (hashcmp(obj_req->sha1, req->real_sha1)) { | |||||
| 537 | ret = error("File %s has bad hash", hex)(error("File %s has bad hash", hex), const_error()); | |||||
| 538 | } else if (req->rename < 0) { | |||||
| 539 | ret = error("unable to write sha1 filename %s",(error("unable to write sha1 filename %s", sha1_file_name(req ->sha1)), const_error()) | |||||
| 540 | sha1_file_name(req->sha1))(error("unable to write sha1 filename %s", sha1_file_name(req ->sha1)), const_error()); | |||||
| 541 | } | |||||
| 542 | ||||||
| 543 | release_http_object_request(req); | |||||
| 544 | release_object_request(obj_req); | |||||
| 545 | return ret; | |||||
| 546 | } | |||||
| 547 | ||||||
| 548 | static int fetch(struct walker *walker, unsigned char *sha1) | |||||
| 549 | { | |||||
| 550 | struct walker_data *data = walker->data; | |||||
| 551 | struct alt_base *altbase = data->alt; | |||||
| 552 | ||||||
| 553 | if (!fetch_object(walker, sha1)) | |||||
| ||||||
| 554 | return 0; | |||||
| 555 | while (altbase) { | |||||
| 556 | if (!http_fetch_pack(walker, altbase, sha1)) | |||||
| 557 | return 0; | |||||
| 558 | fetch_alternates(walker, data->alt->base); | |||||
| 559 | altbase = altbase->next; | |||||
| 560 | } | |||||
| 561 | return error("Unable to find %s under %s", sha1_to_hex(sha1),(error("Unable to find %s under %s", sha1_to_hex(sha1), data-> alt->base), const_error()) | |||||
| ||||||
| 562 | data->alt->base)(error("Unable to find %s under %s", sha1_to_hex(sha1), data-> alt->base), const_error()); | |||||
| 563 | } | |||||
| 564 | ||||||
| 565 | static int fetch_ref(struct walker *walker, struct ref *ref) | |||||
| 566 | { | |||||
| 567 | struct walker_data *data = walker->data; | |||||
| 568 | return http_fetch_ref(data->alt->base, ref); | |||||
| 569 | } | |||||
| 570 | ||||||
| 571 | static void cleanup(struct walker *walker) | |||||
| 572 | { | |||||
| 573 | struct walker_data *data = walker->data; | |||||
| 574 | struct alt_base *alt, *alt_next; | |||||
| 575 | ||||||
| 576 | if (data) { | |||||
| 577 | alt = data->alt; | |||||
| 578 | while (alt) { | |||||
| 579 | alt_next = alt->next; | |||||
| 580 | ||||||
| 581 | free(alt->base); | |||||
| 582 | free(alt); | |||||
| 583 | ||||||
| 584 | alt = alt_next; | |||||
| 585 | } | |||||
| 586 | free(data); | |||||
| 587 | walker->data = NULL((void*)0); | |||||
| 588 | } | |||||
| 589 | } | |||||
| 590 | ||||||
| 591 | struct walker *get_http_walker(const char *url) | |||||
| 592 | { | |||||
| 593 | char *s; | |||||
| 594 | struct walker_data *data = xmalloc(sizeof(struct walker_data)); | |||||
| 595 | struct walker *walker = xmalloc(sizeof(struct walker)); | |||||
| 596 | ||||||
| 597 | data->alt = xmalloc(sizeof(*data->alt)); | |||||
| 598 | data->alt->base = xstrdup(url); | |||||
| 599 | for (s = data->alt->base + strlen(data->alt->base) - 1; *s == '/'; --s) | |||||
| 600 | *s = 0; | |||||
| 601 | ||||||
| 602 | data->alt->got_indices = 0; | |||||
| 603 | data->alt->packs = NULL((void*)0); | |||||
| 604 | data->alt->next = NULL((void*)0); | |||||
| 605 | data->got_alternates = -1; | |||||
| 606 | ||||||
| 607 | walker->corrupt_object_found = 0; | |||||
| 608 | walker->fetch = fetch; | |||||
| 609 | walker->fetch_ref = fetch_ref; | |||||
| 610 | walker->prefetch = prefetch; | |||||
| 611 | walker->cleanup = cleanup; | |||||
| 612 | walker->data = data; | |||||
| 613 | ||||||
| 614 | #ifdef USE_CURL_MULTI | |||||
| 615 | add_fill_function(walker, (int (*)(void *)) fill_active_slot); | |||||
| 616 | #endif | |||||
| 617 | ||||||
| 618 | return walker; | |||||
| 619 | } |