10 /* Definitions for DAV requests */
11 #define DAV_PROPFIND "PROPFIND"
12 #define DAV_PROPFIND_RESP ".multistatus.response"
13 #define DAV_PROPFIND_NAME ".multistatus.response.href"
14 #define DAV_PROPFIND_COLLECTION ".multistatus.response.propstat.prop.resourcetype.collection"
15 #define PROPFIND_ALL_REQUEST "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<D:propfind xmlns:D=\"DAV:\">\n<D:allprop/>\n</D:propfind>"
17 /* Definitions for processing XML DAV responses */
23 #define XML_STATUS_OK 1
24 #define XML_STATUS_ERROR 0
27 /* Flags that control remote_ls processing */
28 #define PROCESS_FILES (1u << 0)
29 #define PROCESS_DIRS (1u << 1)
30 #define RECURSIVE (1u << 2)
32 /* Flags that remote_ls passes to callback functions */
33 #define IS_DIR (1u << 0)
36 #define PREV_BUF_SIZE 4096
37 #define RANGE_HEADER_SIZE 30
39 static int commits_on_stdin;
41 static int got_alternates = -1;
42 static int corrupt_object_found;
44 static struct curl_slist *no_pragma_header;
51 struct packed_git *packs;
52 struct alt_base *next;
55 static struct alt_base *alt;
57 enum object_request_state {
66 unsigned char sha1[20];
67 struct alt_base *repo;
69 char filename[PATH_MAX];
70 char tmpfile[PATH_MAX];
72 enum object_request_state state;
74 char errorstr[CURL_ERROR_SIZE];
76 unsigned char real_sha1[20];
81 struct active_request_slot *slot;
82 struct object_request *next;
85 struct alternates_request {
88 struct buffer *buffer;
89 struct active_request_slot *slot;
99 void (*userFunc)(struct xml_ctx *ctx, int tag_closed);
105 struct alt_base *repo;
107 void (*userFunc)(struct remote_ls_ctx *ls);
113 struct remote_ls_ctx *parent;
117 static struct object_request *object_queue_head;
119 static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
122 unsigned char expn[4096];
123 size_t size = eltsize * nmemb;
125 struct object_request *obj_req = (struct object_request *)data;
127 ssize_t retval = write(obj_req->local,
128 (char *) ptr + posn, size - posn);
132 } while (posn < size);
134 obj_req->stream.avail_in = size;
135 obj_req->stream.next_in = ptr;
137 obj_req->stream.next_out = expn;
138 obj_req->stream.avail_out = sizeof(expn);
139 obj_req->zret = inflate(&obj_req->stream, Z_SYNC_FLUSH);
140 SHA1_Update(&obj_req->c, expn,
141 sizeof(expn) - obj_req->stream.avail_out);
142 } while (obj_req->stream.avail_in && obj_req->zret == Z_OK);
147 static int missing__target(int code, int result)
149 return /* file:// URL -- do we ever use one??? */
150 (result == CURLE_FILE_COULDNT_READ_FILE) ||
151 /* http:// and https:// URL */
152 (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) ||
154 (code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE)
158 #define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
160 static void fetch_alternates(const char *base);
162 static void process_object_response(void *callback_data);
164 static void start_object_request(struct object_request *obj_req)
166 char *hex = sha1_to_hex(obj_req->sha1);
167 char prevfile[PATH_MAX];
171 unsigned char prev_buf[PREV_BUF_SIZE];
172 ssize_t prev_read = 0;
174 char range[RANGE_HEADER_SIZE];
175 struct curl_slist *range_header = NULL;
176 struct active_request_slot *slot;
178 snprintf(prevfile, sizeof(prevfile), "%s.prev", obj_req->filename);
180 rename(obj_req->tmpfile, prevfile);
181 unlink(obj_req->tmpfile);
183 if (obj_req->local != -1)
184 error("fd leakage in start: %d", obj_req->local);
185 obj_req->local = open(obj_req->tmpfile,
186 O_WRONLY | O_CREAT | O_EXCL, 0666);
187 /* This could have failed due to the "lazy directory creation";
188 * try to mkdir the last path component.
190 if (obj_req->local < 0 && errno == ENOENT) {
191 char *dir = strrchr(obj_req->tmpfile, '/');
194 mkdir(obj_req->tmpfile, 0777);
197 obj_req->local = open(obj_req->tmpfile,
198 O_WRONLY | O_CREAT | O_EXCL, 0666);
201 if (obj_req->local < 0) {
202 obj_req->state = ABORTED;
203 error("Couldn't create temporary file %s for %s: %s",
204 obj_req->tmpfile, obj_req->filename, strerror(errno));
208 memset(&obj_req->stream, 0, sizeof(obj_req->stream));
210 inflateInit(&obj_req->stream);
212 SHA1_Init(&obj_req->c);
214 url = xmalloc(strlen(obj_req->repo->base) + 50);
215 obj_req->url = xmalloc(strlen(obj_req->repo->base) + 50);
216 strcpy(url, obj_req->repo->base);
217 posn = url + strlen(obj_req->repo->base);
218 strcpy(posn, "objects/");
220 memcpy(posn, hex, 2);
223 strcpy(posn, hex + 2);
224 strcpy(obj_req->url, url);
226 /* If a previous temp file is present, process what was already
228 prevlocal = open(prevfile, O_RDONLY);
229 if (prevlocal != -1) {
231 prev_read = read(prevlocal, prev_buf, PREV_BUF_SIZE);
233 if (fwrite_sha1_file(prev_buf,
236 obj_req) == prev_read) {
237 prev_posn += prev_read;
242 } while (prev_read > 0);
247 /* Reset inflate/SHA1 if there was an error reading the previous temp
248 file; also rewind to the beginning of the local file. */
249 if (prev_read == -1) {
250 memset(&obj_req->stream, 0, sizeof(obj_req->stream));
251 inflateInit(&obj_req->stream);
252 SHA1_Init(&obj_req->c);
255 lseek(obj_req->local, SEEK_SET, 0);
256 ftruncate(obj_req->local, 0);
260 slot = get_active_slot();
261 slot->callback_func = process_object_response;
262 slot->callback_data = obj_req;
263 obj_req->slot = slot;
265 curl_easy_setopt(slot->curl, CURLOPT_FILE, obj_req);
266 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
267 curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, obj_req->errorstr);
268 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
269 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
271 /* If we have successfully processed data from a previous fetch
272 attempt, only fetch the data we don't already have. */
276 "Resuming fetch of object %s at byte %ld\n",
278 sprintf(range, "Range: bytes=%ld-", prev_posn);
279 range_header = curl_slist_append(range_header, range);
280 curl_easy_setopt(slot->curl,
281 CURLOPT_HTTPHEADER, range_header);
284 /* Try to get the request started, abort the request on error */
285 obj_req->state = ACTIVE;
286 if (!start_active_slot(slot)) {
287 obj_req->state = ABORTED;
288 obj_req->slot = NULL;
289 close(obj_req->local); obj_req->local = -1;
295 static void finish_object_request(struct object_request *obj_req)
299 fchmod(obj_req->local, 0444);
300 close(obj_req->local); obj_req->local = -1;
302 if (obj_req->http_code == 416) {
303 fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
304 } else if (obj_req->curl_result != CURLE_OK) {
305 if (stat(obj_req->tmpfile, &st) == 0)
307 unlink(obj_req->tmpfile);
311 inflateEnd(&obj_req->stream);
312 SHA1_Final(obj_req->real_sha1, &obj_req->c);
313 if (obj_req->zret != Z_STREAM_END) {
314 unlink(obj_req->tmpfile);
317 if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
318 unlink(obj_req->tmpfile);
322 move_temp_to_file(obj_req->tmpfile, obj_req->filename);
324 if (obj_req->rename == 0)
325 pull_say("got %s\n", sha1_to_hex(obj_req->sha1));
328 static void process_object_response(void *callback_data)
330 struct object_request *obj_req =
331 (struct object_request *)callback_data;
333 obj_req->curl_result = obj_req->slot->curl_result;
334 obj_req->http_code = obj_req->slot->http_code;
335 obj_req->slot = NULL;
336 obj_req->state = COMPLETE;
338 /* Use alternates if necessary */
339 if (missing_target(obj_req)) {
340 fetch_alternates(alt->base);
341 if (obj_req->repo->next != NULL) {
344 close(obj_req->local);
346 start_object_request(obj_req);
351 finish_object_request(obj_req);
354 static void release_object_request(struct object_request *obj_req)
356 struct object_request *entry = object_queue_head;
358 if (obj_req->local != -1)
359 error("fd leakage in release: %d", obj_req->local);
360 if (obj_req == object_queue_head) {
361 object_queue_head = obj_req->next;
363 while (entry->next != NULL && entry->next != obj_req)
365 if (entry->next == obj_req)
366 entry->next = entry->next->next;
373 #ifdef USE_CURL_MULTI
374 void fill_active_slots(void)
376 struct object_request *obj_req = object_queue_head;
377 struct active_request_slot *slot = active_queue_head;
380 while (active_requests < max_requests && obj_req != NULL) {
381 if (obj_req->state == WAITING) {
382 if (has_sha1_file(obj_req->sha1))
383 obj_req->state = COMPLETE;
385 start_object_request(obj_req);
386 curl_multi_perform(curlm, &num_transfers);
388 obj_req = obj_req->next;
391 while (slot != NULL) {
392 if (!slot->in_use && slot->curl != NULL) {
393 curl_easy_cleanup(slot->curl);
401 void prefetch(unsigned char *sha1)
403 struct object_request *newreq;
404 struct object_request *tail;
405 char *filename = sha1_file_name(sha1);
407 newreq = xmalloc(sizeof(*newreq));
408 hashcpy(newreq->sha1, sha1);
412 newreq->state = WAITING;
413 snprintf(newreq->filename, sizeof(newreq->filename), "%s", filename);
414 snprintf(newreq->tmpfile, sizeof(newreq->tmpfile),
415 "%s.temp", filename);
419 if (object_queue_head == NULL) {
420 object_queue_head = newreq;
422 tail = object_queue_head;
423 while (tail->next != NULL) {
429 #ifdef USE_CURL_MULTI
435 static int fetch_index(struct alt_base *repo, unsigned char *sha1)
437 char *hex = sha1_to_hex(sha1);
440 char tmpfile[PATH_MAX];
442 char range[RANGE_HEADER_SIZE];
443 struct curl_slist *range_header = NULL;
446 struct active_request_slot *slot;
447 struct slot_results results;
449 if (has_pack_index(sha1))
453 fprintf(stderr, "Getting index for pack %s\n", hex);
455 url = xmalloc(strlen(repo->base) + 64);
456 sprintf(url, "%s/objects/pack/pack-%s.idx", repo->base, hex);
458 filename = sha1_pack_index_name(sha1);
459 snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
460 indexfile = fopen(tmpfile, "a");
462 return error("Unable to open local file %s for pack index",
465 slot = get_active_slot();
466 slot->results = &results;
467 curl_easy_setopt(slot->curl, CURLOPT_FILE, indexfile);
468 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
469 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
470 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
471 slot->local = indexfile;
473 /* If there is data present from a previous transfer attempt,
474 resume where it left off */
475 prev_posn = ftell(indexfile);
479 "Resuming fetch of index for pack %s at byte %ld\n",
481 sprintf(range, "Range: bytes=%ld-", prev_posn);
482 range_header = curl_slist_append(range_header, range);
483 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
486 if (start_active_slot(slot)) {
487 run_active_slot(slot);
488 if (results.curl_result != CURLE_OK) {
490 return error("Unable to get pack index %s\n%s", url,
495 return error("Unable to start request");
500 return move_temp_to_file(tmpfile, filename);
503 static int setup_index(struct alt_base *repo, unsigned char *sha1)
505 struct packed_git *new_pack;
506 if (has_pack_file(sha1))
507 return 0; /* don't list this as something we can get */
509 if (fetch_index(repo, sha1))
512 new_pack = parse_pack_index(sha1);
513 new_pack->next = repo->packs;
514 repo->packs = new_pack;
518 static void process_alternates_response(void *callback_data)
520 struct alternates_request *alt_req =
521 (struct alternates_request *)callback_data;
522 struct active_request_slot *slot = alt_req->slot;
523 struct alt_base *tail = alt;
524 const char *base = alt_req->base;
525 static const char null_byte = '\0';
529 if (alt_req->http_specific) {
530 if (slot->curl_result != CURLE_OK ||
531 !alt_req->buffer->posn) {
533 /* Try reusing the slot to get non-http alternates */
534 alt_req->http_specific = 0;
535 sprintf(alt_req->url, "%s/objects/info/alternates",
537 curl_easy_setopt(slot->curl, CURLOPT_URL,
541 if (slot->finished != NULL)
542 (*slot->finished) = 0;
543 if (!start_active_slot(slot)) {
546 if (slot->finished != NULL)
547 (*slot->finished) = 1;
551 } else if (slot->curl_result != CURLE_OK) {
552 if (!missing_target(slot)) {
558 fwrite_buffer(&null_byte, 1, 1, alt_req->buffer);
559 alt_req->buffer->posn--;
560 data = alt_req->buffer->buffer;
562 while (i < alt_req->buffer->posn) {
564 while (posn < alt_req->buffer->posn && data[posn] != '\n')
566 if (data[posn] == '\n') {
569 struct alt_base *newalt;
572 if (data[i] == '/') {
574 * http://git.host/pub/scm/linux.git/
576 * so memcpy(dst, base, serverlen) will
577 * copy up to "...git.host".
579 const char *colon_ss = strstr(base,"://");
581 serverlen = (strchr(colon_ss + 3, '/')
585 } else if (!memcmp(data + i, "../", 3)) {
586 /* Relative URL; chop the corresponding
587 * number of subpath from base (and ../
588 * from data), and concatenate the result.
590 * The code first drops ../ from data, and
591 * then drops one ../ from data and one path
592 * from base. IOW, one extra ../ is dropped
593 * from data than path is dropped from base.
595 * This is not wrong. The alternate in
596 * http://git.host/pub/scm/linux.git/
598 * http://git.host/pub/scm/linus.git/
599 * is ../../linus.git/objects/. You need
600 * two ../../ to borrow from your direct
604 serverlen = strlen(base);
605 while (i + 2 < posn &&
606 !memcmp(data + i, "../", 3)) {
609 } while (serverlen &&
610 base[serverlen - 1] != '/');
613 /* If the server got removed, give up. */
614 okay = strchr(base, ':') - base + 3 <
616 } else if (alt_req->http_specific) {
617 char *colon = strchr(data + i, ':');
618 char *slash = strchr(data + i, '/');
619 if (colon && slash && colon < data + posn &&
620 slash < data + posn && colon < slash) {
624 /* skip "objects\n" at end */
626 target = xmalloc(serverlen + posn - i - 6);
627 memcpy(target, base, serverlen);
628 memcpy(target + serverlen, data + i,
630 target[serverlen + posn - i - 7] = 0;
633 "Also look at %s\n", target);
634 newalt = xmalloc(sizeof(*newalt));
636 newalt->base = target;
637 newalt->got_indices = 0;
638 newalt->packs = NULL;
639 path = strstr(target, "//");
641 path = strchr(path+2, '/');
643 newalt->path_len = strlen(path);
646 while (tail->next != NULL)
657 static void fetch_alternates(const char *base)
659 struct buffer buffer;
662 struct active_request_slot *slot;
663 struct alternates_request alt_req;
665 /* If another request has already started fetching alternates,
666 wait for them to arrive and return to processing this request's
668 #ifdef USE_CURL_MULTI
669 while (got_alternates == 0) {
674 /* Nothing to do if they've already been fetched */
675 if (got_alternates == 1)
678 /* Start the fetch */
681 data = xmalloc(4096);
684 buffer.buffer = data;
687 fprintf(stderr, "Getting alternates list for %s\n", base);
689 url = xmalloc(strlen(base) + 31);
690 sprintf(url, "%s/objects/info/http-alternates", base);
692 /* Use a callback to process the result, since another request
693 may fail and need to have alternates loaded before continuing */
694 slot = get_active_slot();
695 slot->callback_func = process_alternates_response;
696 slot->callback_data = &alt_req;
698 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
699 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
700 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
704 alt_req.buffer = &buffer;
705 alt_req.http_specific = 1;
708 if (start_active_slot(slot))
709 run_active_slot(slot);
719 xml_start_tag(void *userData, const char *name, const char **atts)
721 struct xml_ctx *ctx = (struct xml_ctx *)userData;
722 const char *c = strchr(name, ':');
730 new_len = strlen(ctx->name) + strlen(c) + 2;
732 if (new_len > ctx->len) {
733 ctx->name = xrealloc(ctx->name, new_len);
736 strcat(ctx->name, ".");
737 strcat(ctx->name, c);
742 ctx->userFunc(ctx, 0);
746 xml_end_tag(void *userData, const char *name)
748 struct xml_ctx *ctx = (struct xml_ctx *)userData;
749 const char *c = strchr(name, ':');
752 ctx->userFunc(ctx, 1);
759 ep = ctx->name + strlen(ctx->name) - strlen(c) - 1;
764 xml_cdata(void *userData, const XML_Char *s, int len)
766 struct xml_ctx *ctx = (struct xml_ctx *)userData;
768 ctx->cdata = xmalloc(len + 1);
769 strlcpy(ctx->cdata, s, len + 1);
772 static int remote_ls(struct alt_base *repo, const char *path, int flags,
773 void (*userFunc)(struct remote_ls_ctx *ls),
776 static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
778 struct remote_ls_ctx *ls = (struct remote_ls_ctx *)ctx->userData;
781 if (!strcmp(ctx->name, DAV_PROPFIND_RESP) && ls->dentry_name) {
782 if (ls->dentry_flags & IS_DIR) {
783 if (ls->flags & PROCESS_DIRS) {
786 if (strcmp(ls->dentry_name, ls->path) &&
787 ls->flags & RECURSIVE) {
788 ls->rc = remote_ls(ls->repo,
794 } else if (ls->flags & PROCESS_FILES) {
797 } else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {
798 ls->dentry_name = xmalloc(strlen(ctx->cdata) -
799 ls->repo->path_len + 1);
800 strcpy(ls->dentry_name, ctx->cdata + ls->repo->path_len);
801 } else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
802 ls->dentry_flags |= IS_DIR;
804 } else if (!strcmp(ctx->name, DAV_PROPFIND_RESP)) {
805 free(ls->dentry_name);
806 ls->dentry_name = NULL;
807 ls->dentry_flags = 0;
811 static int remote_ls(struct alt_base *repo, const char *path, int flags,
812 void (*userFunc)(struct remote_ls_ctx *ls),
815 char *url = xmalloc(strlen(repo->base) + strlen(path) + 1);
816 struct active_request_slot *slot;
817 struct slot_results results;
818 struct buffer in_buffer;
819 struct buffer out_buffer;
822 XML_Parser parser = XML_ParserCreate(NULL);
823 enum XML_Status result;
824 struct curl_slist *dav_headers = NULL;
826 struct remote_ls_ctx ls;
830 ls.path = xstrdup(path);
831 ls.dentry_name = NULL;
833 ls.userData = userData;
834 ls.userFunc = userFunc;
837 sprintf(url, "%s%s", repo->base, path);
839 out_buffer.size = strlen(PROPFIND_ALL_REQUEST);
840 out_data = xmalloc(out_buffer.size + 1);
841 snprintf(out_data, out_buffer.size + 1, PROPFIND_ALL_REQUEST);
843 out_buffer.buffer = out_data;
845 in_buffer.size = 4096;
846 in_data = xmalloc(in_buffer.size);
848 in_buffer.buffer = in_data;
850 dav_headers = curl_slist_append(dav_headers, "Depth: 1");
851 dav_headers = curl_slist_append(dav_headers, "Content-Type: text/xml");
853 slot = get_active_slot();
854 slot->results = &results;
855 curl_easy_setopt(slot->curl, CURLOPT_INFILE, &out_buffer);
856 curl_easy_setopt(slot->curl, CURLOPT_INFILESIZE, out_buffer.size);
857 curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, fread_buffer);
858 curl_easy_setopt(slot->curl, CURLOPT_FILE, &in_buffer);
859 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
860 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
861 curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 1);
862 curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, DAV_PROPFIND);
863 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, dav_headers);
865 if (start_active_slot(slot)) {
866 run_active_slot(slot);
867 if (results.curl_result == CURLE_OK) {
868 ctx.name = xcalloc(10, 1);
871 ctx.userFunc = handle_remote_ls_ctx;
873 XML_SetUserData(parser, &ctx);
874 XML_SetElementHandler(parser, xml_start_tag,
876 XML_SetCharacterDataHandler(parser, xml_cdata);
877 result = XML_Parse(parser, in_buffer.buffer,
881 if (result != XML_STATUS_OK) {
882 ls.rc = error("XML error: %s",
884 XML_GetErrorCode(parser)));
890 ls.rc = error("Unable to start PROPFIND request");
896 free(in_buffer.buffer);
897 curl_slist_free_all(dav_headers);
902 static void process_ls_pack(struct remote_ls_ctx *ls)
904 unsigned char sha1[20];
906 if (strlen(ls->dentry_name) == 63 &&
907 !strncmp(ls->dentry_name, "objects/pack/pack-", 18) &&
908 has_extension(ls->dentry_name, ".pack")) {
909 get_sha1_hex(ls->dentry_name + 18, sha1);
910 setup_index(ls->repo, sha1);
915 static int fetch_indices(struct alt_base *repo)
917 unsigned char sha1[20];
919 struct buffer buffer;
923 struct active_request_slot *slot;
924 struct slot_results results;
926 if (repo->got_indices)
929 data = xmalloc(4096);
932 buffer.buffer = data;
935 fprintf(stderr, "Getting pack list for %s\n", repo->base);
938 if (remote_ls(repo, "objects/pack/", PROCESS_FILES,
939 process_ls_pack, NULL) == 0)
943 url = xmalloc(strlen(repo->base) + 21);
944 sprintf(url, "%s/objects/info/packs", repo->base);
946 slot = get_active_slot();
947 slot->results = &results;
948 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
949 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
950 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
951 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
952 if (start_active_slot(slot)) {
953 run_active_slot(slot);
954 if (results.curl_result != CURLE_OK) {
955 if (missing_target(&results)) {
956 repo->got_indices = 1;
960 repo->got_indices = 0;
962 return error("%s", curl_errorstr);
966 repo->got_indices = 0;
968 return error("Unable to start request");
971 data = buffer.buffer;
972 while (i < buffer.posn) {
976 if (i + 52 <= buffer.posn &&
977 !strncmp(data + i, " pack-", 6) &&
978 !strncmp(data + i + 46, ".pack\n", 6)) {
979 get_sha1_hex(data + i + 6, sha1);
980 setup_index(repo, sha1);
985 while (i < buffer.posn && data[i] != '\n')
992 repo->got_indices = 1;
996 static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
999 struct packed_git *target;
1000 struct packed_git **lst;
1003 char tmpfile[PATH_MAX];
1006 char range[RANGE_HEADER_SIZE];
1007 struct curl_slist *range_header = NULL;
1009 struct active_request_slot *slot;
1010 struct slot_results results;
1012 if (fetch_indices(repo))
1014 target = find_sha1_pack(sha1, repo->packs);
1018 if (get_verbosely) {
1019 fprintf(stderr, "Getting pack %s\n",
1020 sha1_to_hex(target->sha1));
1021 fprintf(stderr, " which contains %s\n",
1025 url = xmalloc(strlen(repo->base) + 65);
1026 sprintf(url, "%s/objects/pack/pack-%s.pack",
1027 repo->base, sha1_to_hex(target->sha1));
1029 filename = sha1_pack_name(target->sha1);
1030 snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
1031 packfile = fopen(tmpfile, "a");
1033 return error("Unable to open local file %s for pack",
1036 slot = get_active_slot();
1037 slot->results = &results;
1038 curl_easy_setopt(slot->curl, CURLOPT_FILE, packfile);
1039 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
1040 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
1041 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
1042 slot->local = packfile;
1044 /* If there is data present from a previous transfer attempt,
1045 resume where it left off */
1046 prev_posn = ftell(packfile);
1050 "Resuming fetch of pack %s at byte %ld\n",
1051 sha1_to_hex(target->sha1), prev_posn);
1052 sprintf(range, "Range: bytes=%ld-", prev_posn);
1053 range_header = curl_slist_append(range_header, range);
1054 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
1057 if (start_active_slot(slot)) {
1058 run_active_slot(slot);
1059 if (results.curl_result != CURLE_OK) {
1061 return error("Unable to get pack file %s\n%s", url,
1066 return error("Unable to start request");
1071 ret = move_temp_to_file(tmpfile, filename);
1076 while (*lst != target)
1077 lst = &((*lst)->next);
1078 *lst = (*lst)->next;
1080 if (verify_pack(target, 0))
1082 install_packed_git(target);
1087 static void abort_object_request(struct object_request *obj_req)
1089 if (obj_req->local >= 0) {
1090 close(obj_req->local);
1091 obj_req->local = -1;
1093 unlink(obj_req->tmpfile);
1094 if (obj_req->slot) {
1095 release_active_slot(obj_req->slot);
1096 obj_req->slot = NULL;
1098 release_object_request(obj_req);
1101 static int fetch_object(struct alt_base *repo, unsigned char *sha1)
1103 char *hex = sha1_to_hex(sha1);
1105 struct object_request *obj_req = object_queue_head;
1107 while (obj_req != NULL && hashcmp(obj_req->sha1, sha1))
1108 obj_req = obj_req->next;
1109 if (obj_req == NULL)
1110 return error("Couldn't find request for %s in the queue", hex);
1112 if (has_sha1_file(obj_req->sha1)) {
1113 abort_object_request(obj_req);
1117 #ifdef USE_CURL_MULTI
1118 while (obj_req->state == WAITING) {
1119 step_active_slots();
1122 start_object_request(obj_req);
1125 while (obj_req->state == ACTIVE) {
1126 run_active_slot(obj_req->slot);
1128 if (obj_req->local != -1) {
1129 close(obj_req->local); obj_req->local = -1;
1132 if (obj_req->state == ABORTED) {
1133 ret = error("Request for %s aborted", hex);
1134 } else if (obj_req->curl_result != CURLE_OK &&
1135 obj_req->http_code != 416) {
1136 if (missing_target(obj_req))
1137 ret = -1; /* Be silent, it is probably in a pack. */
1139 ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
1140 obj_req->errorstr, obj_req->curl_result,
1141 obj_req->http_code, hex);
1142 } else if (obj_req->zret != Z_STREAM_END) {
1143 corrupt_object_found++;
1144 ret = error("File %s (%s) corrupt", hex, obj_req->url);
1145 } else if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
1146 ret = error("File %s has bad hash", hex);
1147 } else if (obj_req->rename < 0) {
1148 ret = error("unable to write sha1 filename %s",
1152 release_object_request(obj_req);
1156 int fetch(unsigned char *sha1)
1158 struct alt_base *altbase = alt;
1160 if (!fetch_object(altbase, sha1))
1163 if (!fetch_pack(altbase, sha1))
1165 fetch_alternates(alt->base);
1166 altbase = altbase->next;
1168 return error("Unable to find %s under %s", sha1_to_hex(sha1),
1172 static inline int needs_quote(int ch)
1174 if (((ch >= 'A') && (ch <= 'Z'))
1175 || ((ch >= 'a') && (ch <= 'z'))
1176 || ((ch >= '0') && (ch <= '9'))
1184 static inline int hex(int v)
1186 if (v < 10) return '0' + v;
1187 else return 'A' + v - 10;
1190 static char *quote_ref_url(const char *base, const char *ref)
1194 int len, baselen, ch;
1196 baselen = strlen(base);
1197 len = baselen + 6; /* "refs/" + NUL */
1198 for (cp = ref; (ch = *cp) != 0; cp++, len++)
1199 if (needs_quote(ch))
1200 len += 2; /* extra two hex plus replacement % */
1201 qref = xmalloc(len);
1202 memcpy(qref, base, baselen);
1203 memcpy(qref + baselen, "refs/", 5);
1204 for (cp = ref, dp = qref + baselen + 5; (ch = *cp) != 0; cp++) {
1205 if (needs_quote(ch)) {
1207 *dp++ = hex((ch >> 4) & 0xF);
1208 *dp++ = hex(ch & 0xF);
1218 int fetch_ref(char *ref, unsigned char *sha1)
1222 struct buffer buffer;
1223 const char *base = alt->base;
1224 struct active_request_slot *slot;
1225 struct slot_results results;
1228 buffer.buffer = hex;
1231 url = quote_ref_url(base, ref);
1232 slot = get_active_slot();
1233 slot->results = &results;
1234 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
1235 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
1236 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
1237 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
1238 if (start_active_slot(slot)) {
1239 run_active_slot(slot);
1240 if (results.curl_result != CURLE_OK)
1241 return error("Couldn't get %s for %s\n%s",
1242 url, ref, curl_errorstr);
1244 return error("Unable to start request");
1248 get_sha1_hex(hex, sha1);
1252 int main(int argc, const char **argv)
1255 const char **write_ref = NULL;
1263 setup_git_directory();
1264 git_config(git_default_config);
1266 while (arg < argc && argv[arg][0] == '-') {
1267 if (argv[arg][1] == 't') {
1269 } else if (argv[arg][1] == 'c') {
1271 } else if (argv[arg][1] == 'a') {
1275 } else if (argv[arg][1] == 'v') {
1277 } else if (argv[arg][1] == 'w') {
1278 write_ref = &argv[arg + 1];
1280 } else if (!strcmp(argv[arg], "--recover")) {
1282 } else if (!strcmp(argv[arg], "--stdin")) {
1283 commits_on_stdin = 1;
1287 if (argc < arg + 2 - commits_on_stdin) {
1288 usage("git-http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url");
1291 if (commits_on_stdin) {
1292 commits = pull_targets_stdin(&commit_id, &write_ref);
1294 commit_id = (char **) &argv[arg++];
1301 no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
1303 alt = xmalloc(sizeof(*alt));
1305 alt->got_indices = 0;
1308 path = strstr(url, "//");
1310 path = strchr(path+2, '/');
1312 alt->path_len = strlen(path);
1315 if (pull(commits, commit_id, write_ref, url))
1320 curl_slist_free_all(no_pragma_header);
1322 if (commits_on_stdin)
1323 pull_targets_free(commits, commit_id, write_ref);
1325 if (corrupt_object_found) {
1327 "Some loose object were found to be corrupt, but they might be just\n"
1328 "a false '404 Not Found' error message sent with incorrect HTTP\n"
1329 "status code. Suggest running git fsck-objects.\n");