]> git.ipfire.org Git - thirdparty/git.git/blame - http-fetch.c
cvsimport: use git-update-ref when updating
[thirdparty/git.git] / http-fetch.c
CommitLineData
6eb7ed54
DB
1#include "cache.h"
2#include "commit.h"
271421cd 3#include "pack.h"
215a7ad1 4#include "fetch.h"
29508e1e 5#include "http.h"
7baa3e86 6
49a0f240
NH
7#define PREV_BUF_SIZE 4096
8#define RANGE_HEADER_SIZE 30
9
acc075a8 10static int got_alternates = -1;
bb528079 11static int corrupt_object_found = 0;
1d389ab6 12
1db69b57 13static struct curl_slist *no_pragma_header;
6eb7ed54 14
b3661567
DB
15struct alt_base
16{
17 char *base;
18 int got_indices;
19 struct packed_git *packs;
20 struct alt_base *next;
21};
22
a7928f8e 23static struct alt_base *alt = NULL;
6eb7ed54 24
e388ab74 25enum object_request_state {
1d389ab6
NH
26 WAITING,
27 ABORTED,
28 ACTIVE,
29 COMPLETE,
30};
6eb7ed54 31
e388ab74 32struct object_request
1d389ab6
NH
33{
34 unsigned char sha1[20];
35 struct alt_base *repo;
36 char *url;
37 char filename[PATH_MAX];
38 char tmpfile[PATH_MAX];
39 int local;
e388ab74 40 enum object_request_state state;
1d389ab6
NH
41 CURLcode curl_result;
42 char errorstr[CURL_ERROR_SIZE];
43 long http_code;
44 unsigned char real_sha1[20];
45 SHA_CTX c;
46 z_stream stream;
47 int zret;
48 int rename;
49 struct active_request_slot *slot;
e388ab74 50 struct object_request *next;
1d389ab6
NH
51};
52
e388ab74 53struct alternates_request {
acc075a8
NH
54 char *base;
55 char *url;
56 struct buffer *buffer;
57 struct active_request_slot *slot;
58 int http_specific;
59};
60
e388ab74 61static struct object_request *object_queue_head = NULL;
bc8f2652 62
182005b9
DB
63static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
64 void *data)
65{
bf0f910d 66 unsigned char expn[4096];
6eb7ed54
DB
67 size_t size = eltsize * nmemb;
68 int posn = 0;
e388ab74 69 struct object_request *obj_req = (struct object_request *)data;
6eb7ed54 70 do {
e388ab74 71 ssize_t retval = write(obj_req->local,
1d389ab6 72 ptr + posn, size - posn);
6eb7ed54
DB
73 if (retval < 0)
74 return posn;
75 posn += retval;
76 } while (posn < size);
77
e388ab74
NH
78 obj_req->stream.avail_in = size;
79 obj_req->stream.next_in = ptr;
6eb7ed54 80 do {
e388ab74
NH
81 obj_req->stream.next_out = expn;
82 obj_req->stream.avail_out = sizeof(expn);
83 obj_req->zret = inflate(&obj_req->stream, Z_SYNC_FLUSH);
84 SHA1_Update(&obj_req->c, expn,
85 sizeof(expn) - obj_req->stream.avail_out);
86 } while (obj_req->stream.avail_in && obj_req->zret == Z_OK);
1d389ab6 87 data_received++;
6eb7ed54
DB
88 return size;
89}
90
acc075a8 91static void fetch_alternates(char *base);
1d389ab6 92
29508e1e 93static void process_object_response(void *callback_data);
1d389ab6 94
e388ab74 95static void start_object_request(struct object_request *obj_req)
1d389ab6 96{
e388ab74 97 char *hex = sha1_to_hex(obj_req->sha1);
1d389ab6
NH
98 char prevfile[PATH_MAX];
99 char *url;
100 char *posn;
101 int prevlocal;
102 unsigned char prev_buf[PREV_BUF_SIZE];
103 ssize_t prev_read = 0;
104 long prev_posn = 0;
105 char range[RANGE_HEADER_SIZE];
106 struct curl_slist *range_header = NULL;
107 struct active_request_slot *slot;
108
e388ab74 109 snprintf(prevfile, sizeof(prevfile), "%s.prev", obj_req->filename);
1d389ab6 110 unlink(prevfile);
e388ab74
NH
111 rename(obj_req->tmpfile, prevfile);
112 unlink(obj_req->tmpfile);
1d389ab6 113
e388ab74
NH
114 if (obj_req->local != -1)
115 error("fd leakage in start: %d", obj_req->local);
116 obj_req->local = open(obj_req->tmpfile,
1d389ab6 117 O_WRONLY | O_CREAT | O_EXCL, 0666);
b721e01f
JH
118 /* This could have failed due to the "lazy directory creation";
119 * try to mkdir the last path component.
120 */
e388ab74
NH
121 if (obj_req->local < 0 && errno == ENOENT) {
122 char *dir = strrchr(obj_req->tmpfile, '/');
b721e01f
JH
123 if (dir) {
124 *dir = 0;
e388ab74 125 mkdir(obj_req->tmpfile, 0777);
b721e01f
JH
126 *dir = '/';
127 }
e388ab74 128 obj_req->local = open(obj_req->tmpfile,
b721e01f
JH
129 O_WRONLY | O_CREAT | O_EXCL, 0666);
130 }
131
e388ab74
NH
132 if (obj_req->local < 0) {
133 obj_req->state = ABORTED;
bd2afde8 134 error("Couldn't create temporary file %s for %s: %s",
e388ab74 135 obj_req->tmpfile, obj_req->filename, strerror(errno));
1d389ab6
NH
136 return;
137 }
138
e388ab74 139 memset(&obj_req->stream, 0, sizeof(obj_req->stream));
1d389ab6 140
e388ab74 141 inflateInit(&obj_req->stream);
1d389ab6 142
e388ab74 143 SHA1_Init(&obj_req->c);
1d389ab6 144
e388ab74
NH
145 url = xmalloc(strlen(obj_req->repo->base) + 50);
146 obj_req->url = xmalloc(strlen(obj_req->repo->base) + 50);
147 strcpy(url, obj_req->repo->base);
148 posn = url + strlen(obj_req->repo->base);
1d389ab6
NH
149 strcpy(posn, "objects/");
150 posn += 8;
151 memcpy(posn, hex, 2);
152 posn += 2;
153 *(posn++) = '/';
154 strcpy(posn, hex + 2);
e388ab74 155 strcpy(obj_req->url, url);
1d389ab6
NH
156
157 /* If a previous temp file is present, process what was already
158 fetched. */
159 prevlocal = open(prevfile, O_RDONLY);
160 if (prevlocal != -1) {
161 do {
162 prev_read = read(prevlocal, prev_buf, PREV_BUF_SIZE);
163 if (prev_read>0) {
164 if (fwrite_sha1_file(prev_buf,
165 1,
166 prev_read,
e388ab74 167 obj_req) == prev_read) {
1d389ab6
NH
168 prev_posn += prev_read;
169 } else {
170 prev_read = -1;
171 }
172 }
173 } while (prev_read > 0);
174 close(prevlocal);
175 }
176 unlink(prevfile);
177
178 /* Reset inflate/SHA1 if there was an error reading the previous temp
179 file; also rewind to the beginning of the local file. */
180 if (prev_read == -1) {
e388ab74
NH
181 memset(&obj_req->stream, 0, sizeof(obj_req->stream));
182 inflateInit(&obj_req->stream);
183 SHA1_Init(&obj_req->c);
1d389ab6
NH
184 if (prev_posn>0) {
185 prev_posn = 0;
e388ab74
NH
186 lseek(obj_req->local, SEEK_SET, 0);
187 ftruncate(obj_req->local, 0);
1d389ab6
NH
188 }
189 }
190
191 slot = get_active_slot();
29508e1e 192 slot->callback_func = process_object_response;
e388ab74
NH
193 slot->callback_data = obj_req;
194 obj_req->slot = slot;
29508e1e 195
e388ab74 196 curl_easy_setopt(slot->curl, CURLOPT_FILE, obj_req);
1d389ab6 197 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
e388ab74 198 curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, obj_req->errorstr);
1d389ab6 199 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
03126006 200 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
1d389ab6
NH
201
202 /* If we have successfully processed data from a previous fetch
203 attempt, only fetch the data we don't already have. */
204 if (prev_posn>0) {
205 if (get_verbosely)
206 fprintf(stderr,
207 "Resuming fetch of object %s at byte %ld\n",
208 hex, prev_posn);
209 sprintf(range, "Range: bytes=%ld-", prev_posn);
210 range_header = curl_slist_append(range_header, range);
211 curl_easy_setopt(slot->curl,
212 CURLOPT_HTTPHEADER, range_header);
213 }
214
a7a8d378 215 /* Try to get the request started, abort the request on error */
e388ab74 216 obj_req->state = ACTIVE;
1d389ab6 217 if (!start_active_slot(slot)) {
e388ab74
NH
218 obj_req->state = ABORTED;
219 obj_req->slot = NULL;
220 close(obj_req->local); obj_req->local = -1;
221 free(obj_req->url);
222 return;
1d389ab6 223 }
1d389ab6
NH
224}
225
e388ab74 226static void finish_object_request(struct object_request *obj_req)
1d389ab6 227{
50496b21
NH
228 struct stat st;
229
e388ab74
NH
230 fchmod(obj_req->local, 0444);
231 close(obj_req->local); obj_req->local = -1;
1d389ab6 232
e388ab74 233 if (obj_req->http_code == 416) {
1d389ab6 234 fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
e388ab74
NH
235 } else if (obj_req->curl_result != CURLE_OK) {
236 if (stat(obj_req->tmpfile, &st) == 0)
50496b21 237 if (st.st_size == 0)
e388ab74 238 unlink(obj_req->tmpfile);
1d389ab6
NH
239 return;
240 }
241
e388ab74
NH
242 inflateEnd(&obj_req->stream);
243 SHA1_Final(obj_req->real_sha1, &obj_req->c);
244 if (obj_req->zret != Z_STREAM_END) {
245 unlink(obj_req->tmpfile);
1d389ab6
NH
246 return;
247 }
e388ab74
NH
248 if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) {
249 unlink(obj_req->tmpfile);
1d389ab6
NH
250 return;
251 }
e388ab74
NH
252 obj_req->rename =
253 move_temp_to_file(obj_req->tmpfile, obj_req->filename);
1d389ab6 254
e388ab74
NH
255 if (obj_req->rename == 0)
256 pull_say("got %s\n", sha1_to_hex(obj_req->sha1));
1d389ab6
NH
257}
258
29508e1e
NH
259static void process_object_response(void *callback_data)
260{
e388ab74
NH
261 struct object_request *obj_req =
262 (struct object_request *)callback_data;
29508e1e 263
e388ab74
NH
264 obj_req->curl_result = obj_req->slot->curl_result;
265 obj_req->http_code = obj_req->slot->http_code;
266 obj_req->slot = NULL;
267 obj_req->state = COMPLETE;
29508e1e
NH
268
269 /* Use alternates if necessary */
a14c2256
NH
270 if (obj_req->http_code == 404 ||
271 obj_req->curl_result == CURLE_FILE_COULDNT_READ_FILE) {
29508e1e 272 fetch_alternates(alt->base);
e388ab74
NH
273 if (obj_req->repo->next != NULL) {
274 obj_req->repo =
275 obj_req->repo->next;
276 close(obj_req->local);
277 obj_req->local = -1;
278 start_object_request(obj_req);
29508e1e
NH
279 return;
280 }
281 }
282
e388ab74 283 finish_object_request(obj_req);
29508e1e
NH
284}
285
e388ab74 286static void release_object_request(struct object_request *obj_req)
1d389ab6 287{
e388ab74 288 struct object_request *entry = object_queue_head;
1d389ab6 289
e388ab74
NH
290 if (obj_req->local != -1)
291 error("fd leakage in release: %d", obj_req->local);
292 if (obj_req == object_queue_head) {
293 object_queue_head = obj_req->next;
1d389ab6 294 } else {
e388ab74 295 while (entry->next != NULL && entry->next != obj_req)
1d389ab6 296 entry = entry->next;
e388ab74 297 if (entry->next == obj_req)
1d389ab6
NH
298 entry->next = entry->next->next;
299 }
300
e388ab74
NH
301 free(obj_req->url);
302 free(obj_req);
1d389ab6
NH
303}
304
a7a8d378 305#ifdef USE_CURL_MULTI
29508e1e 306void fill_active_slots(void)
1d389ab6 307{
e388ab74 308 struct object_request *obj_req = object_queue_head;
f1a906a3 309 struct active_request_slot *slot = active_queue_head;
1d389ab6
NH
310 int num_transfers;
311
e388ab74
NH
312 while (active_requests < max_requests && obj_req != NULL) {
313 if (obj_req->state == WAITING) {
314 if (has_sha1_file(obj_req->sha1))
09db444f 315 obj_req->state = COMPLETE;
11f0dafe 316 else
e388ab74 317 start_object_request(obj_req);
1d389ab6
NH
318 curl_multi_perform(curlm, &num_transfers);
319 }
e388ab74 320 obj_req = obj_req->next;
1d389ab6 321 }
f1a906a3
NH
322
323 while (slot != NULL) {
324 if (!slot->in_use && slot->curl != NULL) {
325 curl_easy_cleanup(slot->curl);
326 slot->curl = NULL;
327 }
328 slot = slot->next;
8fcf7f9a 329 }
1d389ab6 330}
a7a8d378 331#endif
1d389ab6
NH
332
333void prefetch(unsigned char *sha1)
334{
e388ab74
NH
335 struct object_request *newreq;
336 struct object_request *tail;
1d389ab6
NH
337 char *filename = sha1_file_name(sha1);
338
339 newreq = xmalloc(sizeof(*newreq));
340 memcpy(newreq->sha1, sha1, 20);
341 newreq->repo = alt;
342 newreq->url = NULL;
343 newreq->local = -1;
344 newreq->state = WAITING;
345 snprintf(newreq->filename, sizeof(newreq->filename), "%s", filename);
346 snprintf(newreq->tmpfile, sizeof(newreq->tmpfile),
347 "%s.temp", filename);
348 newreq->next = NULL;
349
e388ab74
NH
350 if (object_queue_head == NULL) {
351 object_queue_head = newreq;
1d389ab6 352 } else {
e388ab74 353 tail = object_queue_head;
1d389ab6
NH
354 while (tail->next != NULL) {
355 tail = tail->next;
356 }
357 tail->next = newreq;
358 }
29508e1e 359
a7a8d378 360#ifdef USE_CURL_MULTI
29508e1e
NH
361 fill_active_slots();
362 step_active_slots();
a7a8d378 363#endif
1d389ab6
NH
364}
365
b3661567 366static int fetch_index(struct alt_base *repo, unsigned char *sha1)
182005b9 367{
1d389ab6 368 char *hex = sha1_to_hex(sha1);
182005b9
DB
369 char *filename;
370 char *url;
49a0f240 371 char tmpfile[PATH_MAX];
49a0f240
NH
372 long prev_posn = 0;
373 char range[RANGE_HEADER_SIZE];
374 struct curl_slist *range_header = NULL;
182005b9
DB
375
376 FILE *indexfile;
1d389ab6 377 struct active_request_slot *slot;
cb754fdf 378 struct slot_results results;
182005b9
DB
379
380 if (has_pack_index(sha1))
381 return 0;
382
383 if (get_verbosely)
1d389ab6 384 fprintf(stderr, "Getting index for pack %s\n", hex);
8fcf7f9a 385
b3661567 386 url = xmalloc(strlen(repo->base) + 64);
1d389ab6 387 sprintf(url, "%s/objects/pack/pack-%s.idx", repo->base, hex);
8fcf7f9a 388
182005b9 389 filename = sha1_pack_index_name(sha1);
49a0f240
NH
390 snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
391 indexfile = fopen(tmpfile, "a");
182005b9
DB
392 if (!indexfile)
393 return error("Unable to open local file %s for pack index",
394 filename);
395
1d389ab6 396 slot = get_active_slot();
c8568e13 397 slot->results = &results;
1d389ab6
NH
398 curl_easy_setopt(slot->curl, CURLOPT_FILE, indexfile);
399 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
400 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
03126006 401 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
1d389ab6
NH
402 slot->local = indexfile;
403
49a0f240
NH
404 /* If there is data present from a previous transfer attempt,
405 resume where it left off */
406 prev_posn = ftell(indexfile);
407 if (prev_posn>0) {
408 if (get_verbosely)
409 fprintf(stderr,
410 "Resuming fetch of index for pack %s at byte %ld\n",
1d389ab6 411 hex, prev_posn);
49a0f240
NH
412 sprintf(range, "Range: bytes=%ld-", prev_posn);
413 range_header = curl_slist_append(range_header, range);
1d389ab6 414 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
49a0f240
NH
415 }
416
1d389ab6
NH
417 if (start_active_slot(slot)) {
418 run_active_slot(slot);
c8568e13 419 if (results.curl_result != CURLE_OK) {
1d389ab6
NH
420 fclose(indexfile);
421 return error("Unable to get pack index %s\n%s", url,
422 curl_errorstr);
423 }
424 } else {
313c4714 425 fclose(indexfile);
1d389ab6 426 return error("Unable to start request");
182005b9
DB
427 }
428
429 fclose(indexfile);
49a0f240 430
b721e01f 431 return move_temp_to_file(tmpfile, filename);
182005b9
DB
432}
433
b3661567 434static int setup_index(struct alt_base *repo, unsigned char *sha1)
182005b9
DB
435{
436 struct packed_git *new_pack;
437 if (has_pack_file(sha1))
438 return 0; // don't list this as something we can get
439
b3661567 440 if (fetch_index(repo, sha1))
182005b9
DB
441 return -1;
442
443 new_pack = parse_pack_index(sha1);
b3661567
DB
444 new_pack->next = repo->packs;
445 repo->packs = new_pack;
182005b9
DB
446 return 0;
447}
448
e388ab74 449static void process_alternates_response(void *callback_data)
b3661567 450{
e388ab74
NH
451 struct alternates_request *alt_req =
452 (struct alternates_request *)callback_data;
acc075a8 453 struct active_request_slot *slot = alt_req->slot;
1d389ab6 454 struct alt_base *tail = alt;
acc075a8 455 char *base = alt_req->base;
bc8f2652 456 static const char null_byte = '\0';
acc075a8
NH
457 char *data;
458 int i = 0;
1d389ab6 459
acc075a8
NH
460 if (alt_req->http_specific) {
461 if (slot->curl_result != CURLE_OK ||
462 !alt_req->buffer->posn) {
463
464 /* Try reusing the slot to get non-http alternates */
465 alt_req->http_specific = 0;
466 sprintf(alt_req->url, "%s/objects/info/alternates",
467 base);
468 curl_easy_setopt(slot->curl, CURLOPT_URL,
469 alt_req->url);
470 active_requests++;
471 slot->in_use = 1;
c9826473
NH
472 if (slot->finished != NULL)
473 (*slot->finished) = 0;
a3f583cb 474 if (!start_active_slot(slot)) {
acc075a8 475 got_alternates = -1;
29508e1e 476 slot->in_use = 0;
c9826473
NH
477 if (slot->finished != NULL)
478 (*slot->finished) = 1;
1d389ab6 479 }
a3f583cb 480 return;
b3661567 481 }
acc075a8 482 } else if (slot->curl_result != CURLE_OK) {
a14c2256
NH
483 if (slot->http_code != 404 &&
484 slot->curl_result != CURLE_FILE_COULDNT_READ_FILE) {
acc075a8
NH
485 got_alternates = -1;
486 return;
487 }
b3661567
DB
488 }
489
29508e1e 490 fwrite_buffer(&null_byte, 1, 1, alt_req->buffer);
acc075a8
NH
491 alt_req->buffer->posn--;
492 data = alt_req->buffer->buffer;
1b0c1e67 493
acc075a8 494 while (i < alt_req->buffer->posn) {
b3661567 495 int posn = i;
acc075a8 496 while (posn < alt_req->buffer->posn && data[posn] != '\n')
b3661567
DB
497 posn++;
498 if (data[posn] == '\n') {
1b0c1e67
DB
499 int okay = 0;
500 int serverlen = 0;
501 struct alt_base *newalt;
502 char *target = NULL;
b3661567 503 if (data[i] == '/') {
1b0c1e67
DB
504 serverlen = strchr(base + 8, '/') - base;
505 okay = 1;
506 } else if (!memcmp(data + i, "../", 3)) {
507 i += 3;
508 serverlen = strlen(base);
8fcf7f9a 509 while (i + 2 < posn &&
1b0c1e67
DB
510 !memcmp(data + i, "../", 3)) {
511 do {
512 serverlen--;
513 } while (serverlen &&
514 base[serverlen - 1] != '/');
515 i += 3;
516 }
517 // If the server got removed, give up.
8fcf7f9a 518 okay = strchr(base, ':') - base + 3 <
1b0c1e67 519 serverlen;
acc075a8 520 } else if (alt_req->http_specific) {
1b0c1e67
DB
521 char *colon = strchr(data + i, ':');
522 char *slash = strchr(data + i, '/');
523 if (colon && slash && colon < data + posn &&
524 slash < data + posn && colon < slash) {
525 okay = 1;
526 }
527 }
528 // skip 'objects' at end
529 if (okay) {
530 target = xmalloc(serverlen + posn - i - 6);
b3661567
DB
531 strncpy(target, base, serverlen);
532 strncpy(target + serverlen, data + i,
533 posn - i - 7);
534 target[serverlen + posn - i - 7] = '\0';
535 if (get_verbosely)
8fcf7f9a 536 fprintf(stderr,
b3661567
DB
537 "Also look at %s\n", target);
538 newalt = xmalloc(sizeof(*newalt));
1d389ab6 539 newalt->next = NULL;
b3661567
DB
540 newalt->base = target;
541 newalt->got_indices = 0;
542 newalt->packs = NULL;
1d389ab6
NH
543 while (tail->next != NULL)
544 tail = tail->next;
545 tail->next = newalt;
b3661567
DB
546 }
547 }
548 i = posn + 1;
549 }
bc8f2652 550
f7eb290f 551 got_alternates = 1;
acc075a8
NH
552}
553
554static void fetch_alternates(char *base)
555{
556 struct buffer buffer;
557 char *url;
558 char *data;
559 struct active_request_slot *slot;
cb754fdf 560 struct alternates_request alt_req;
acc075a8
NH
561
562 /* If another request has already started fetching alternates,
563 wait for them to arrive and return to processing this request's
564 curl message */
29508e1e 565#ifdef USE_CURL_MULTI
acc075a8 566 while (got_alternates == 0) {
29508e1e 567 step_active_slots();
acc075a8 568 }
29508e1e 569#endif
acc075a8
NH
570
571 /* Nothing to do if they've already been fetched */
572 if (got_alternates == 1)
573 return;
574
575 /* Start the fetch */
576 got_alternates = 0;
577
578 data = xmalloc(4096);
579 buffer.size = 4096;
580 buffer.posn = 0;
581 buffer.buffer = data;
582
583 if (get_verbosely)
584 fprintf(stderr, "Getting alternates list for %s\n", base);
8fcf7f9a 585
acc075a8
NH
586 url = xmalloc(strlen(base) + 31);
587 sprintf(url, "%s/objects/info/http-alternates", base);
588
589 /* Use a callback to process the result, since another request
590 may fail and need to have alternates loaded before continuing */
591 slot = get_active_slot();
e388ab74 592 slot->callback_func = process_alternates_response;
acc075a8
NH
593 slot->callback_data = &alt_req;
594
595 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
29508e1e 596 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
acc075a8
NH
597 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
598
599 alt_req.base = base;
600 alt_req.url = url;
601 alt_req.buffer = &buffer;
602 alt_req.http_specific = 1;
603 alt_req.slot = slot;
604
605 if (start_active_slot(slot))
606 run_active_slot(slot);
607 else
608 got_alternates = -1;
609
610 free(data);
611 free(url);
b3661567
DB
612}
613
614static int fetch_indices(struct alt_base *repo)
182005b9
DB
615{
616 unsigned char sha1[20];
617 char *url;
618 struct buffer buffer;
619 char *data;
620 int i = 0;
621
1d389ab6 622 struct active_request_slot *slot;
cb754fdf 623 struct slot_results results;
1d389ab6 624
b3661567 625 if (repo->got_indices)
182005b9
DB
626 return 0;
627
628 data = xmalloc(4096);
629 buffer.size = 4096;
630 buffer.posn = 0;
631 buffer.buffer = data;
632
633 if (get_verbosely)
6fd72e39 634 fprintf(stderr, "Getting pack list for %s\n", repo->base);
8fcf7f9a 635
b3661567
DB
636 url = xmalloc(strlen(repo->base) + 21);
637 sprintf(url, "%s/objects/info/packs", repo->base);
182005b9 638
1d389ab6 639 slot = get_active_slot();
c8568e13 640 slot->results = &results;
1d389ab6 641 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
29508e1e 642 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
1d389ab6
NH
643 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
644 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
645 if (start_active_slot(slot)) {
646 run_active_slot(slot);
c8568e13
NH
647 if (results.curl_result != CURLE_OK) {
648 if (results.http_code == 404 ||
649 results.curl_result == CURLE_FILE_COULDNT_READ_FILE) {
5e3a7691
NH
650 repo->got_indices = 1;
651 free(buffer.buffer);
652 return 0;
653 } else {
654 repo->got_indices = 0;
655 free(buffer.buffer);
656 return error("%s", curl_errorstr);
657 }
bc8f2652 658 }
1d389ab6 659 } else {
5e3a7691 660 repo->got_indices = 0;
bc8f2652 661 free(buffer.buffer);
1d389ab6
NH
662 return error("Unable to start request");
663 }
182005b9 664
bc8f2652 665 data = buffer.buffer;
b3661567 666 while (i < buffer.posn) {
182005b9
DB
667 switch (data[i]) {
668 case 'P':
669 i++;
455c161c 670 if (i + 52 <= buffer.posn &&
182005b9
DB
671 !strncmp(data + i, " pack-", 6) &&
672 !strncmp(data + i + 46, ".pack\n", 6)) {
673 get_sha1_hex(data + i + 6, sha1);
b3661567 674 setup_index(repo, sha1);
182005b9
DB
675 i += 51;
676 break;
677 }
678 default:
455c161c 679 while (i < buffer.posn && data[i] != '\n')
182005b9
DB
680 i++;
681 }
682 i++;
b3661567 683 }
182005b9 684
bc8f2652 685 free(buffer.buffer);
b3661567 686 repo->got_indices = 1;
182005b9
DB
687 return 0;
688}
689
b3661567 690static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
182005b9
DB
691{
692 char *url;
693 struct packed_git *target;
694 struct packed_git **lst;
695 FILE *packfile;
696 char *filename;
49a0f240
NH
697 char tmpfile[PATH_MAX];
698 int ret;
699 long prev_posn = 0;
700 char range[RANGE_HEADER_SIZE];
701 struct curl_slist *range_header = NULL;
1d389ab6
NH
702
703 struct active_request_slot *slot;
cb754fdf 704 struct slot_results results;
182005b9 705
b3661567 706 if (fetch_indices(repo))
182005b9 707 return -1;
b3661567 708 target = find_sha1_pack(sha1, repo->packs);
182005b9 709 if (!target)
b3661567 710 return -1;
182005b9
DB
711
712 if (get_verbosely) {
713 fprintf(stderr, "Getting pack %s\n",
714 sha1_to_hex(target->sha1));
715 fprintf(stderr, " which contains %s\n",
716 sha1_to_hex(sha1));
717 }
718
b3661567 719 url = xmalloc(strlen(repo->base) + 65);
182005b9 720 sprintf(url, "%s/objects/pack/pack-%s.pack",
b3661567 721 repo->base, sha1_to_hex(target->sha1));
182005b9
DB
722
723 filename = sha1_pack_name(target->sha1);
49a0f240
NH
724 snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
725 packfile = fopen(tmpfile, "a");
182005b9
DB
726 if (!packfile)
727 return error("Unable to open local file %s for pack",
728 filename);
729
1d389ab6 730 slot = get_active_slot();
c8568e13 731 slot->results = &results;
1d389ab6
NH
732 curl_easy_setopt(slot->curl, CURLOPT_FILE, packfile);
733 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
734 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
03126006 735 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
1d389ab6 736 slot->local = packfile;
1ddea77e 737
49a0f240
NH
738 /* If there is data present from a previous transfer attempt,
739 resume where it left off */
740 prev_posn = ftell(packfile);
741 if (prev_posn>0) {
742 if (get_verbosely)
743 fprintf(stderr,
744 "Resuming fetch of pack %s at byte %ld\n",
745 sha1_to_hex(target->sha1), prev_posn);
746 sprintf(range, "Range: bytes=%ld-", prev_posn);
747 range_header = curl_slist_append(range_header, range);
1d389ab6 748 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
49a0f240
NH
749 }
750
1d389ab6
NH
751 if (start_active_slot(slot)) {
752 run_active_slot(slot);
c8568e13 753 if (results.curl_result != CURLE_OK) {
1d389ab6
NH
754 fclose(packfile);
755 return error("Unable to get pack file %s\n%s", url,
756 curl_errorstr);
757 }
758 } else {
313c4714 759 fclose(packfile);
1d389ab6 760 return error("Unable to start request");
182005b9
DB
761 }
762
763 fclose(packfile);
764
b721e01f 765 ret = move_temp_to_file(tmpfile, filename);
49a0f240 766 if (ret)
b721e01f 767 return ret;
49a0f240 768
b3661567 769 lst = &repo->packs;
182005b9
DB
770 while (*lst != target)
771 lst = &((*lst)->next);
772 *lst = (*lst)->next;
773
271421cd
JH
774 if (verify_pack(target, 0))
775 return -1;
182005b9
DB
776 install_packed_git(target);
777
778 return 0;
779}
780
53f31389
MW
781static void abort_object_request(struct object_request *obj_req)
782{
783 if (obj_req->local >= 0) {
784 close(obj_req->local);
785 obj_req->local = -1;
786 }
787 unlink(obj_req->tmpfile);
788 if (obj_req->slot) {
789 release_active_slot(obj_req->slot);
790 obj_req->slot = NULL;
791 }
792 release_object_request(obj_req);
793}
794
a7928f8e 795static int fetch_object(struct alt_base *repo, unsigned char *sha1)
6eb7ed54
DB
796{
797 char *hex = sha1_to_hex(sha1);
29508e1e 798 int ret = 0;
e388ab74 799 struct object_request *obj_req = object_queue_head;
1d389ab6 800
e388ab74
NH
801 while (obj_req != NULL && memcmp(obj_req->sha1, sha1, 20))
802 obj_req = obj_req->next;
803 if (obj_req == NULL)
1d389ab6
NH
804 return error("Couldn't find request for %s in the queue", hex);
805
e388ab74 806 if (has_sha1_file(obj_req->sha1)) {
53f31389 807 abort_object_request(obj_req);
11f0dafe
NH
808 return 0;
809 }
810
a7a8d378 811#ifdef USE_CURL_MULTI
e388ab74 812 while (obj_req->state == WAITING) {
29508e1e 813 step_active_slots();
1d389ab6 814 }
a7a8d378 815#else
e388ab74 816 start_object_request(obj_req);
a7a8d378 817#endif
6eb7ed54 818
e388ab74
NH
819 while (obj_req->state == ACTIVE) {
820 run_active_slot(obj_req->slot);
a7a8d378 821 }
e388ab74
NH
822 if (obj_req->local != -1) {
823 close(obj_req->local); obj_req->local = -1;
313c4714 824 }
6eb7ed54 825
e388ab74 826 if (obj_req->state == ABORTED) {
29508e1e 827 ret = error("Request for %s aborted", hex);
e388ab74
NH
828 } else if (obj_req->curl_result != CURLE_OK &&
829 obj_req->http_code != 416) {
a14c2256
NH
830 if (obj_req->http_code == 404 ||
831 obj_req->curl_result == CURLE_FILE_COULDNT_READ_FILE)
e2029eb9
PB
832 ret = -1; /* Be silent, it is probably in a pack. */
833 else
834 ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
e388ab74
NH
835 obj_req->errorstr, obj_req->curl_result,
836 obj_req->http_code, hex);
837 } else if (obj_req->zret != Z_STREAM_END) {
bb528079 838 corrupt_object_found++;
bd2afde8 839 ret = error("File %s (%s) corrupt", hex, obj_req->url);
e388ab74 840 } else if (memcmp(obj_req->sha1, obj_req->real_sha1, 20)) {
bd2afde8 841 ret = error("File %s has bad hash", hex);
e388ab74 842 } else if (obj_req->rename < 0) {
7b934ec0
MW
843 ret = error("unable to write sha1 filename %s",
844 obj_req->filename);
6eb7ed54 845 }
49a0f240 846
e388ab74 847 release_object_request(obj_req);
29508e1e 848 return ret;
6eb7ed54
DB
849}
850
b3661567
DB
851int fetch(unsigned char *sha1)
852{
853 struct alt_base *altbase = alt;
1d389ab6
NH
854
855 if (!fetch_object(altbase, sha1))
856 return 0;
b3661567 857 while (altbase) {
b3661567
DB
858 if (!fetch_pack(altbase, sha1))
859 return 0;
f7eb290f 860 fetch_alternates(alt->base);
b3661567
DB
861 altbase = altbase->next;
862 }
bd2afde8 863 return error("Unable to find %s under %s", sha1_to_hex(sha1),
1d389ab6 864 alt->base);
b3661567
DB
865}
866
94fa447a
JH
867static inline int needs_quote(int ch)
868{
869 switch (ch) {
870 case '/': case '-': case '.':
871 case 'A'...'Z': case 'a'...'z': case '0'...'9':
872 return 0;
873 default:
874 return 1;
875 }
876}
877
878static inline int hex(int v)
879{
880 if (v < 10) return '0' + v;
881 else return 'A' + v - 10;
882}
883
884static char *quote_ref_url(const char *base, const char *ref)
885{
886 const char *cp;
887 char *dp, *qref;
888 int len, baselen, ch;
889
890 baselen = strlen(base);
891 len = baselen + 6; /* "refs/" + NUL */
892 for (cp = ref; (ch = *cp) != 0; cp++, len++)
893 if (needs_quote(ch))
894 len += 2; /* extra two hex plus replacement % */
895 qref = xmalloc(len);
896 memcpy(qref, base, baselen);
897 memcpy(qref + baselen, "refs/", 5);
898 for (cp = ref, dp = qref + baselen + 5; (ch = *cp) != 0; cp++) {
899 if (needs_quote(ch)) {
900 *dp++ = '%';
901 *dp++ = hex((ch >> 4) & 0xF);
902 *dp++ = hex(ch & 0xF);
903 }
904 else
905 *dp++ = ch;
906 }
907 *dp = 0;
908
909 return qref;
910}
911
cd541a68
DB
912int fetch_ref(char *ref, unsigned char *sha1)
913{
94fa447a 914 char *url;
fa3e0655
DB
915 char hex[42];
916 struct buffer buffer;
1d389ab6
NH
917 char *base = alt->base;
918 struct active_request_slot *slot;
cb754fdf 919 struct slot_results results;
fa3e0655
DB
920 buffer.size = 41;
921 buffer.posn = 0;
922 buffer.buffer = hex;
923 hex[41] = '\0';
8fcf7f9a 924
94fa447a 925 url = quote_ref_url(base, ref);
1d389ab6 926 slot = get_active_slot();
c8568e13 927 slot->results = &results;
1d389ab6
NH
928 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
929 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
930 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
931 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
932 if (start_active_slot(slot)) {
933 run_active_slot(slot);
c8568e13 934 if (results.curl_result != CURLE_OK)
1d389ab6
NH
935 return error("Couldn't get %s for %s\n%s",
936 url, ref, curl_errorstr);
937 } else {
938 return error("Unable to start request");
939 }
fa3e0655
DB
940
941 hex[40] = '\0';
942 get_sha1_hex(hex, sha1);
943 return 0;
cd541a68
DB
944}
945
6eb7ed54
DB
946int main(int argc, char **argv)
947{
948 char *commit_id;
949 char *url;
950 int arg = 1;
7b9ae53e 951 int rc = 0;
6eb7ed54 952
5a327713
JH
953 setup_git_directory();
954
6eb7ed54
DB
955 while (arg < argc && argv[arg][0] == '-') {
956 if (argv[arg][1] == 't') {
4250a5e5 957 get_tree = 1;
6eb7ed54 958 } else if (argv[arg][1] == 'c') {
4250a5e5 959 get_history = 1;
6eb7ed54 960 } else if (argv[arg][1] == 'a') {
4250a5e5
DB
961 get_all = 1;
962 get_tree = 1;
963 get_history = 1;
e78d9772
JH
964 } else if (argv[arg][1] == 'v') {
965 get_verbosely = 1;
fa3e0655
DB
966 } else if (argv[arg][1] == 'w') {
967 write_ref = argv[arg + 1];
968 arg++;
820eca68
DB
969 } else if (!strcmp(argv[arg], "--recover")) {
970 get_recover = 1;
6eb7ed54
DB
971 }
972 arg++;
973 }
974 if (argc < arg + 2) {
215a7ad1 975 usage("git-http-fetch [-c] [-t] [-a] [-d] [-v] [--recover] [-w ref] commit-id url");
6eb7ed54
DB
976 return 1;
977 }
978 commit_id = argv[arg];
979 url = argv[arg + 1];
980
29508e1e 981 http_init();
d402d556 982
1db69b57 983 no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
3dcb90f5 984
b3661567
DB
985 alt = xmalloc(sizeof(*alt));
986 alt->base = url;
987 alt->got_indices = 0;
988 alt->packs = NULL;
989 alt->next = NULL;
6eb7ed54 990
4250a5e5 991 if (pull(commit_id))
7b9ae53e 992 rc = 1;
6eb7ed54 993
1db69b57 994 curl_slist_free_all(no_pragma_header);
29508e1e
NH
995
996 http_cleanup();
997
bb528079
JH
998 if (corrupt_object_found) {
999 fprintf(stderr,
1000"Some loose object were found to be corrupt, but they might be just\n"
1001"a false '404 Not Found' error message sent with incorrect HTTP\n"
1002"status code. Suggest running git fsck-objects.\n");
1003 }
7b9ae53e 1004 return rc;
6eb7ed54 1005}