blob: 202fae0ba8b348aeca6ec6c61045f39aa0d1fa66 [file] [log] [blame]
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -07001#include "cache.h"
2#include "commit.h"
Junio C Hamano271421c2005-09-30 00:07:39 -07003#include "pack.h"
Junio C Hamano215a7ad2005-09-07 17:26:23 -07004#include "fetch.h"
Nick Hengeveld29508e12005-11-18 11:02:58 -08005#include "http.h"
Johannes Schindelin7baa3e82005-10-15 11:10:46 -07006
Nick Hengeveld49a0f242005-09-28 10:14:04 -07007#define PREV_BUF_SIZE 4096
8#define RANGE_HEADER_SIZE 30
9
David Rientjes96f1e582006-08-15 10:23:48 -070010static int commits_on_stdin;
Petr Baudis8e29f6a2006-07-27 23:56:22 +020011
Nick Hengeveldacc075a2005-11-12 09:11:32 -080012static int got_alternates = -1;
David Rientjes96f1e582006-08-15 10:23:48 -070013static int corrupt_object_found;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -070014
Sergey Vlasov1db69b52005-09-13 19:38:58 +040015static struct curl_slist *no_pragma_header;
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -070016
Daniel Barkalowb3661562005-09-14 23:26:08 -040017struct alt_base
18{
Gerrit Pape2afea3b2007-03-28 09:46:15 +000019 char *base;
Daniel Barkalowb3661562005-09-14 23:26:08 -040020 int got_indices;
21 struct packed_git *packs;
22 struct alt_base *next;
23};
24
David Rientjes96f1e582006-08-15 10:23:48 -070025static struct alt_base *alt;
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -070026
Nick Hengevelde388ab72005-11-18 11:03:04 -080027enum object_request_state {
Nick Hengeveld1d389ab2005-10-10 23:22:01 -070028 WAITING,
29 ABORTED,
30 ACTIVE,
31 COMPLETE,
32};
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -070033
Nick Hengevelde388ab72005-11-18 11:03:04 -080034struct object_request
Nick Hengeveld1d389ab2005-10-10 23:22:01 -070035{
36 unsigned char sha1[20];
37 struct alt_base *repo;
38 char *url;
39 char filename[PATH_MAX];
40 char tmpfile[PATH_MAX];
41 int local;
Nick Hengevelde388ab72005-11-18 11:03:04 -080042 enum object_request_state state;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -070043 CURLcode curl_result;
44 char errorstr[CURL_ERROR_SIZE];
45 long http_code;
46 unsigned char real_sha1[20];
47 SHA_CTX c;
48 z_stream stream;
49 int zret;
50 int rename;
51 struct active_request_slot *slot;
Nick Hengevelde388ab72005-11-18 11:03:04 -080052 struct object_request *next;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -070053};
54
Nick Hengevelde388ab72005-11-18 11:03:04 -080055struct alternates_request {
Petr Baudis8e29f6a2006-07-27 23:56:22 +020056 const char *base;
Nick Hengeveldacc075a2005-11-12 09:11:32 -080057 char *url;
58 struct buffer *buffer;
59 struct active_request_slot *slot;
60 int http_specific;
61};
62
David Rientjes96f1e582006-08-15 10:23:48 -070063static struct object_request *object_queue_head;
Sergey Vlasovbc8f2652005-10-13 10:49:53 -070064
barkalow@iabervon.org182005b2005-07-31 20:54:17 -040065static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
66 void *data)
67{
Brian Gerstbf0f9102005-05-18 08:14:09 -040068 unsigned char expn[4096];
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -070069 size_t size = eltsize * nmemb;
70 int posn = 0;
Nick Hengevelde388ab72005-11-18 11:03:04 -080071 struct object_request *obj_req = (struct object_request *)data;
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -070072 do {
Andy Whitcroft93822c22007-01-08 15:58:23 +000073 ssize_t retval = xwrite(obj_req->local,
Florian Forster1d7f1712006-06-18 17:18:09 +020074 (char *) ptr + posn, size - posn);
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -070075 if (retval < 0)
76 return posn;
77 posn += retval;
78 } while (posn < size);
79
Nick Hengevelde388ab72005-11-18 11:03:04 -080080 obj_req->stream.avail_in = size;
81 obj_req->stream.next_in = ptr;
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -070082 do {
Nick Hengevelde388ab72005-11-18 11:03:04 -080083 obj_req->stream.next_out = expn;
84 obj_req->stream.avail_out = sizeof(expn);
85 obj_req->zret = inflate(&obj_req->stream, Z_SYNC_FLUSH);
86 SHA1_Update(&obj_req->c, expn,
87 sizeof(expn) - obj_req->stream.avail_out);
88 } while (obj_req->stream.avail_in && obj_req->zret == Z_OK);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -070089 data_received++;
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -070090 return size;
91}
92
Junio C Hamanobe4a0152006-09-16 10:58:20 -070093static int missing__target(int code, int result)
94{
95 return /* file:// URL -- do we ever use one??? */
96 (result == CURLE_FILE_COULDNT_READ_FILE) ||
97 /* http:// and https:// URL */
Junio C Hamano4adffc72006-09-16 11:06:02 -070098 (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) ||
99 /* ftp:// URL */
100 (code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE)
Junio C Hamanobe4a0152006-09-16 10:58:20 -0700101 ;
102}
103
104#define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
105
Petr Baudis8e29f6a2006-07-27 23:56:22 +0200106static void fetch_alternates(const char *base);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700107
Nick Hengeveld29508e12005-11-18 11:02:58 -0800108static void process_object_response(void *callback_data);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700109
Nick Hengevelde388ab72005-11-18 11:03:04 -0800110static void start_object_request(struct object_request *obj_req)
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700111{
Nick Hengevelde388ab72005-11-18 11:03:04 -0800112 char *hex = sha1_to_hex(obj_req->sha1);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700113 char prevfile[PATH_MAX];
114 char *url;
115 char *posn;
116 int prevlocal;
117 unsigned char prev_buf[PREV_BUF_SIZE];
118 ssize_t prev_read = 0;
119 long prev_posn = 0;
120 char range[RANGE_HEADER_SIZE];
121 struct curl_slist *range_header = NULL;
122 struct active_request_slot *slot;
123
Nick Hengevelde388ab72005-11-18 11:03:04 -0800124 snprintf(prevfile, sizeof(prevfile), "%s.prev", obj_req->filename);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700125 unlink(prevfile);
Nick Hengevelde388ab72005-11-18 11:03:04 -0800126 rename(obj_req->tmpfile, prevfile);
127 unlink(obj_req->tmpfile);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700128
Nick Hengevelde388ab72005-11-18 11:03:04 -0800129 if (obj_req->local != -1)
130 error("fd leakage in start: %d", obj_req->local);
131 obj_req->local = open(obj_req->tmpfile,
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700132 O_WRONLY | O_CREAT | O_EXCL, 0666);
Junio C Hamanob721e012005-10-10 23:22:01 -0700133 /* This could have failed due to the "lazy directory creation";
134 * try to mkdir the last path component.
135 */
Nick Hengevelde388ab72005-11-18 11:03:04 -0800136 if (obj_req->local < 0 && errno == ENOENT) {
137 char *dir = strrchr(obj_req->tmpfile, '/');
Junio C Hamanob721e012005-10-10 23:22:01 -0700138 if (dir) {
139 *dir = 0;
Nick Hengevelde388ab72005-11-18 11:03:04 -0800140 mkdir(obj_req->tmpfile, 0777);
Junio C Hamanob721e012005-10-10 23:22:01 -0700141 *dir = '/';
142 }
Nick Hengevelde388ab72005-11-18 11:03:04 -0800143 obj_req->local = open(obj_req->tmpfile,
Junio C Hamanob721e012005-10-10 23:22:01 -0700144 O_WRONLY | O_CREAT | O_EXCL, 0666);
145 }
146
Nick Hengevelde388ab72005-11-18 11:03:04 -0800147 if (obj_req->local < 0) {
148 obj_req->state = ABORTED;
Junio C Hamanobd2afde2006-02-22 17:47:10 -0800149 error("Couldn't create temporary file %s for %s: %s",
Nick Hengevelde388ab72005-11-18 11:03:04 -0800150 obj_req->tmpfile, obj_req->filename, strerror(errno));
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700151 return;
152 }
153
Nick Hengevelde388ab72005-11-18 11:03:04 -0800154 memset(&obj_req->stream, 0, sizeof(obj_req->stream));
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700155
Nick Hengevelde388ab72005-11-18 11:03:04 -0800156 inflateInit(&obj_req->stream);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700157
Nick Hengevelde388ab72005-11-18 11:03:04 -0800158 SHA1_Init(&obj_req->c);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700159
Gerrit Pape2afea3b2007-03-28 09:46:15 +0000160 url = xmalloc(strlen(obj_req->repo->base) + 51);
161 obj_req->url = xmalloc(strlen(obj_req->repo->base) + 51);
Nick Hengevelde388ab72005-11-18 11:03:04 -0800162 strcpy(url, obj_req->repo->base);
163 posn = url + strlen(obj_req->repo->base);
Gerrit Pape2afea3b2007-03-28 09:46:15 +0000164 strcpy(posn, "/objects/");
165 posn += 9;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700166 memcpy(posn, hex, 2);
167 posn += 2;
168 *(posn++) = '/';
169 strcpy(posn, hex + 2);
Nick Hengevelde388ab72005-11-18 11:03:04 -0800170 strcpy(obj_req->url, url);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700171
172 /* If a previous temp file is present, process what was already
173 fetched. */
174 prevlocal = open(prevfile, O_RDONLY);
175 if (prevlocal != -1) {
176 do {
Andy Whitcroft93d26e42007-01-08 15:58:08 +0000177 prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700178 if (prev_read>0) {
179 if (fwrite_sha1_file(prev_buf,
180 1,
181 prev_read,
Nick Hengevelde388ab72005-11-18 11:03:04 -0800182 obj_req) == prev_read) {
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700183 prev_posn += prev_read;
184 } else {
185 prev_read = -1;
186 }
187 }
188 } while (prev_read > 0);
189 close(prevlocal);
190 }
191 unlink(prevfile);
192
193 /* Reset inflate/SHA1 if there was an error reading the previous temp
194 file; also rewind to the beginning of the local file. */
195 if (prev_read == -1) {
Nick Hengevelde388ab72005-11-18 11:03:04 -0800196 memset(&obj_req->stream, 0, sizeof(obj_req->stream));
197 inflateInit(&obj_req->stream);
198 SHA1_Init(&obj_req->c);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700199 if (prev_posn>0) {
200 prev_posn = 0;
Dana Howb5da2462007-04-05 12:05:57 -0700201 lseek(obj_req->local, 0, SEEK_SET);
Nick Hengevelde388ab72005-11-18 11:03:04 -0800202 ftruncate(obj_req->local, 0);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700203 }
204 }
205
206 slot = get_active_slot();
Nick Hengeveld29508e12005-11-18 11:02:58 -0800207 slot->callback_func = process_object_response;
Nick Hengevelde388ab72005-11-18 11:03:04 -0800208 slot->callback_data = obj_req;
209 obj_req->slot = slot;
Nick Hengeveld29508e12005-11-18 11:02:58 -0800210
Nick Hengevelde388ab72005-11-18 11:03:04 -0800211 curl_easy_setopt(slot->curl, CURLOPT_FILE, obj_req);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700212 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
Nick Hengevelde388ab72005-11-18 11:03:04 -0800213 curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, obj_req->errorstr);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700214 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
Nick Hengeveld03126002005-10-10 23:22:01 -0700215 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700216
217 /* If we have successfully processed data from a previous fetch
218 attempt, only fetch the data we don't already have. */
219 if (prev_posn>0) {
220 if (get_verbosely)
221 fprintf(stderr,
222 "Resuming fetch of object %s at byte %ld\n",
223 hex, prev_posn);
224 sprintf(range, "Range: bytes=%ld-", prev_posn);
225 range_header = curl_slist_append(range_header, range);
226 curl_easy_setopt(slot->curl,
227 CURLOPT_HTTPHEADER, range_header);
228 }
229
Nick Hengevelda7a8d372005-10-10 23:22:01 -0700230 /* Try to get the request started, abort the request on error */
Nick Hengevelde388ab72005-11-18 11:03:04 -0800231 obj_req->state = ACTIVE;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700232 if (!start_active_slot(slot)) {
Nick Hengevelde388ab72005-11-18 11:03:04 -0800233 obj_req->state = ABORTED;
234 obj_req->slot = NULL;
235 close(obj_req->local); obj_req->local = -1;
236 free(obj_req->url);
237 return;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700238 }
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700239}
240
Nick Hengevelde388ab72005-11-18 11:03:04 -0800241static void finish_object_request(struct object_request *obj_req)
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700242{
Nick Hengeveld50496b22005-11-03 17:54:52 -0800243 struct stat st;
244
Nick Hengevelde388ab72005-11-18 11:03:04 -0800245 fchmod(obj_req->local, 0444);
246 close(obj_req->local); obj_req->local = -1;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700247
Nick Hengevelde388ab72005-11-18 11:03:04 -0800248 if (obj_req->http_code == 416) {
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700249 fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
Nick Hengevelde388ab72005-11-18 11:03:04 -0800250 } else if (obj_req->curl_result != CURLE_OK) {
251 if (stat(obj_req->tmpfile, &st) == 0)
Nick Hengeveld50496b22005-11-03 17:54:52 -0800252 if (st.st_size == 0)
Nick Hengevelde388ab72005-11-18 11:03:04 -0800253 unlink(obj_req->tmpfile);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700254 return;
255 }
256
Nick Hengevelde388ab72005-11-18 11:03:04 -0800257 inflateEnd(&obj_req->stream);
258 SHA1_Final(obj_req->real_sha1, &obj_req->c);
259 if (obj_req->zret != Z_STREAM_END) {
260 unlink(obj_req->tmpfile);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700261 return;
262 }
David Rientjesa89fccd2006-08-17 11:54:57 -0700263 if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
Nick Hengevelde388ab72005-11-18 11:03:04 -0800264 unlink(obj_req->tmpfile);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700265 return;
266 }
Nick Hengevelde388ab72005-11-18 11:03:04 -0800267 obj_req->rename =
268 move_temp_to_file(obj_req->tmpfile, obj_req->filename);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700269
Nick Hengevelde388ab72005-11-18 11:03:04 -0800270 if (obj_req->rename == 0)
271 pull_say("got %s\n", sha1_to_hex(obj_req->sha1));
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700272}
273
Nick Hengeveld29508e12005-11-18 11:02:58 -0800274static void process_object_response(void *callback_data)
275{
Nick Hengevelde388ab72005-11-18 11:03:04 -0800276 struct object_request *obj_req =
277 (struct object_request *)callback_data;
Nick Hengeveld29508e12005-11-18 11:02:58 -0800278
Nick Hengevelde388ab72005-11-18 11:03:04 -0800279 obj_req->curl_result = obj_req->slot->curl_result;
280 obj_req->http_code = obj_req->slot->http_code;
281 obj_req->slot = NULL;
282 obj_req->state = COMPLETE;
Nick Hengeveld29508e12005-11-18 11:02:58 -0800283
284 /* Use alternates if necessary */
Junio C Hamanobe4a0152006-09-16 10:58:20 -0700285 if (missing_target(obj_req)) {
Nick Hengeveld29508e12005-11-18 11:02:58 -0800286 fetch_alternates(alt->base);
Nick Hengevelde388ab72005-11-18 11:03:04 -0800287 if (obj_req->repo->next != NULL) {
288 obj_req->repo =
289 obj_req->repo->next;
290 close(obj_req->local);
291 obj_req->local = -1;
292 start_object_request(obj_req);
Nick Hengeveld29508e12005-11-18 11:02:58 -0800293 return;
294 }
295 }
296
Nick Hengevelde388ab72005-11-18 11:03:04 -0800297 finish_object_request(obj_req);
Nick Hengeveld29508e12005-11-18 11:02:58 -0800298}
299
Nick Hengevelde388ab72005-11-18 11:03:04 -0800300static void release_object_request(struct object_request *obj_req)
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700301{
Nick Hengevelde388ab72005-11-18 11:03:04 -0800302 struct object_request *entry = object_queue_head;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700303
Nick Hengevelde388ab72005-11-18 11:03:04 -0800304 if (obj_req->local != -1)
305 error("fd leakage in release: %d", obj_req->local);
306 if (obj_req == object_queue_head) {
307 object_queue_head = obj_req->next;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700308 } else {
Nick Hengevelde388ab72005-11-18 11:03:04 -0800309 while (entry->next != NULL && entry->next != obj_req)
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700310 entry = entry->next;
Nick Hengevelde388ab72005-11-18 11:03:04 -0800311 if (entry->next == obj_req)
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700312 entry->next = entry->next->next;
313 }
314
Nick Hengevelde388ab72005-11-18 11:03:04 -0800315 free(obj_req->url);
316 free(obj_req);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700317}
318
Nick Hengevelda7a8d372005-10-10 23:22:01 -0700319#ifdef USE_CURL_MULTI
Nick Hengeveld29508e12005-11-18 11:02:58 -0800320void fill_active_slots(void)
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700321{
Nick Hengevelde388ab72005-11-18 11:03:04 -0800322 struct object_request *obj_req = object_queue_head;
Nick Hengeveldf1a906a2005-10-21 12:06:10 -0700323 struct active_request_slot *slot = active_queue_head;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700324 int num_transfers;
325
Nick Hengevelde388ab72005-11-18 11:03:04 -0800326 while (active_requests < max_requests && obj_req != NULL) {
327 if (obj_req->state == WAITING) {
328 if (has_sha1_file(obj_req->sha1))
Mark Wooding09db4442006-02-01 11:44:28 +0000329 obj_req->state = COMPLETE;
Nick Hengeveld11f0daf2005-10-10 23:22:01 -0700330 else
Nick Hengevelde388ab72005-11-18 11:03:04 -0800331 start_object_request(obj_req);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700332 curl_multi_perform(curlm, &num_transfers);
333 }
Nick Hengevelde388ab72005-11-18 11:03:04 -0800334 obj_req = obj_req->next;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700335 }
Nick Hengeveldf1a906a2005-10-21 12:06:10 -0700336
337 while (slot != NULL) {
338 if (!slot->in_use && slot->curl != NULL) {
339 curl_easy_cleanup(slot->curl);
340 slot->curl = NULL;
341 }
342 slot = slot->next;
Junio C Hamano8fcf7f92006-01-31 18:15:51 -0800343 }
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700344}
Nick Hengevelda7a8d372005-10-10 23:22:01 -0700345#endif
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700346
347void prefetch(unsigned char *sha1)
348{
Nick Hengevelde388ab72005-11-18 11:03:04 -0800349 struct object_request *newreq;
350 struct object_request *tail;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700351 char *filename = sha1_file_name(sha1);
352
353 newreq = xmalloc(sizeof(*newreq));
Shawn Pearcee7024962006-08-23 02:49:00 -0400354 hashcpy(newreq->sha1, sha1);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700355 newreq->repo = alt;
356 newreq->url = NULL;
357 newreq->local = -1;
358 newreq->state = WAITING;
359 snprintf(newreq->filename, sizeof(newreq->filename), "%s", filename);
360 snprintf(newreq->tmpfile, sizeof(newreq->tmpfile),
361 "%s.temp", filename);
Nick Hengevelde8dff6b2006-06-06 22:22:35 -0700362 newreq->slot = NULL;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700363 newreq->next = NULL;
364
Nick Hengevelde388ab72005-11-18 11:03:04 -0800365 if (object_queue_head == NULL) {
366 object_queue_head = newreq;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700367 } else {
Nick Hengevelde388ab72005-11-18 11:03:04 -0800368 tail = object_queue_head;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700369 while (tail->next != NULL) {
370 tail = tail->next;
371 }
372 tail->next = newreq;
373 }
Nick Hengeveld29508e12005-11-18 11:02:58 -0800374
Nick Hengevelda7a8d372005-10-10 23:22:01 -0700375#ifdef USE_CURL_MULTI
Nick Hengeveld29508e12005-11-18 11:02:58 -0800376 fill_active_slots();
377 step_active_slots();
Nick Hengevelda7a8d372005-10-10 23:22:01 -0700378#endif
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700379}
380
Daniel Barkalowb3661562005-09-14 23:26:08 -0400381static int fetch_index(struct alt_base *repo, unsigned char *sha1)
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400382{
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700383 char *hex = sha1_to_hex(sha1);
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400384 char *filename;
385 char *url;
Nick Hengeveld49a0f242005-09-28 10:14:04 -0700386 char tmpfile[PATH_MAX];
Nick Hengeveld49a0f242005-09-28 10:14:04 -0700387 long prev_posn = 0;
388 char range[RANGE_HEADER_SIZE];
389 struct curl_slist *range_header = NULL;
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400390
391 FILE *indexfile;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700392 struct active_request_slot *slot;
Nick Hengeveldcb754fd2006-01-31 18:00:37 -0800393 struct slot_results results;
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400394
395 if (has_pack_index(sha1))
396 return 0;
397
398 if (get_verbosely)
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700399 fprintf(stderr, "Getting index for pack %s\n", hex);
Junio C Hamano8fcf7f92006-01-31 18:15:51 -0800400
Daniel Barkalowb3661562005-09-14 23:26:08 -0400401 url = xmalloc(strlen(repo->base) + 64);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700402 sprintf(url, "%s/objects/pack/pack-%s.idx", repo->base, hex);
Junio C Hamano8fcf7f92006-01-31 18:15:51 -0800403
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400404 filename = sha1_pack_index_name(sha1);
Nick Hengeveld49a0f242005-09-28 10:14:04 -0700405 snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
406 indexfile = fopen(tmpfile, "a");
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400407 if (!indexfile)
408 return error("Unable to open local file %s for pack index",
409 filename);
410
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700411 slot = get_active_slot();
Nick Hengeveldc8568e12006-01-31 11:06:55 -0800412 slot->results = &results;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700413 curl_easy_setopt(slot->curl, CURLOPT_FILE, indexfile);
414 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
415 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
Nick Hengeveld03126002005-10-10 23:22:01 -0700416 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700417 slot->local = indexfile;
418
Nick Hengeveld49a0f242005-09-28 10:14:04 -0700419 /* If there is data present from a previous transfer attempt,
420 resume where it left off */
421 prev_posn = ftell(indexfile);
422 if (prev_posn>0) {
423 if (get_verbosely)
424 fprintf(stderr,
425 "Resuming fetch of index for pack %s at byte %ld\n",
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700426 hex, prev_posn);
Nick Hengeveld49a0f242005-09-28 10:14:04 -0700427 sprintf(range, "Range: bytes=%ld-", prev_posn);
428 range_header = curl_slist_append(range_header, range);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700429 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
Nick Hengeveld49a0f242005-09-28 10:14:04 -0700430 }
431
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700432 if (start_active_slot(slot)) {
433 run_active_slot(slot);
Nick Hengeveldc8568e12006-01-31 11:06:55 -0800434 if (results.curl_result != CURLE_OK) {
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700435 fclose(indexfile);
436 return error("Unable to get pack index %s\n%s", url,
437 curl_errorstr);
438 }
439 } else {
Petr Baudis313c4712005-11-12 00:55:16 +0100440 fclose(indexfile);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700441 return error("Unable to start request");
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400442 }
443
444 fclose(indexfile);
Nick Hengeveld49a0f242005-09-28 10:14:04 -0700445
Junio C Hamanob721e012005-10-10 23:22:01 -0700446 return move_temp_to_file(tmpfile, filename);
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400447}
448
Daniel Barkalowb3661562005-09-14 23:26:08 -0400449static int setup_index(struct alt_base *repo, unsigned char *sha1)
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400450{
451 struct packed_git *new_pack;
452 if (has_pack_file(sha1))
Pavel Roskina9486b02006-07-10 02:57:51 -0400453 return 0; /* don't list this as something we can get */
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400454
Daniel Barkalowb3661562005-09-14 23:26:08 -0400455 if (fetch_index(repo, sha1))
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400456 return -1;
457
458 new_pack = parse_pack_index(sha1);
Daniel Barkalowb3661562005-09-14 23:26:08 -0400459 new_pack->next = repo->packs;
460 repo->packs = new_pack;
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400461 return 0;
462}
463
Nick Hengevelde388ab72005-11-18 11:03:04 -0800464static void process_alternates_response(void *callback_data)
Daniel Barkalowb3661562005-09-14 23:26:08 -0400465{
Nick Hengevelde388ab72005-11-18 11:03:04 -0800466 struct alternates_request *alt_req =
467 (struct alternates_request *)callback_data;
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800468 struct active_request_slot *slot = alt_req->slot;
469 struct alt_base *tail = alt;
Petr Baudis8e29f6a2006-07-27 23:56:22 +0200470 const char *base = alt_req->base;
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800471 static const char null_byte = '\0';
Daniel Barkalowb3661562005-09-14 23:26:08 -0400472 char *data;
473 int i = 0;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700474
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800475 if (alt_req->http_specific) {
476 if (slot->curl_result != CURLE_OK ||
477 !alt_req->buffer->posn) {
Junio C Hamanodc1b5ea2005-10-10 23:22:02 -0700478
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800479 /* Try reusing the slot to get non-http alternates */
480 alt_req->http_specific = 0;
481 sprintf(alt_req->url, "%s/objects/info/alternates",
482 base);
483 curl_easy_setopt(slot->curl, CURLOPT_URL,
484 alt_req->url);
485 active_requests++;
486 slot->in_use = 1;
Nick Hengeveldc9826472006-03-15 08:59:52 -0800487 if (slot->finished != NULL)
488 (*slot->finished) = 0;
Mark Woodinga3f583c2006-02-01 11:44:39 +0000489 if (!start_active_slot(slot)) {
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800490 got_alternates = -1;
Nick Hengeveld29508e12005-11-18 11:02:58 -0800491 slot->in_use = 0;
Nick Hengeveldc9826472006-03-15 08:59:52 -0800492 if (slot->finished != NULL)
493 (*slot->finished) = 1;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700494 }
Mark Woodinga3f583c2006-02-01 11:44:39 +0000495 return;
Daniel Barkalowb3661562005-09-14 23:26:08 -0400496 }
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800497 } else if (slot->curl_result != CURLE_OK) {
Junio C Hamanobe4a0152006-09-16 10:58:20 -0700498 if (!missing_target(slot)) {
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800499 got_alternates = -1;
500 return;
501 }
Daniel Barkalowb3661562005-09-14 23:26:08 -0400502 }
503
Nick Hengeveld29508e12005-11-18 11:02:58 -0800504 fwrite_buffer(&null_byte, 1, 1, alt_req->buffer);
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800505 alt_req->buffer->posn--;
506 data = alt_req->buffer->buffer;
Daniel Barkalow1b0c1e62005-09-18 14:14:19 -0400507
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800508 while (i < alt_req->buffer->posn) {
Daniel Barkalowb3661562005-09-14 23:26:08 -0400509 int posn = i;
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800510 while (posn < alt_req->buffer->posn && data[posn] != '\n')
Daniel Barkalowb3661562005-09-14 23:26:08 -0400511 posn++;
512 if (data[posn] == '\n') {
Daniel Barkalow1b0c1e62005-09-18 14:14:19 -0400513 int okay = 0;
514 int serverlen = 0;
515 struct alt_base *newalt;
516 char *target = NULL;
Daniel Barkalowb3661562005-09-14 23:26:08 -0400517 if (data[i] == '/') {
Junio C Hamano5df1e0d2006-09-12 23:53:27 -0700518 /* This counts
519 * http://git.host/pub/scm/linux.git/
520 * -----------here^
521 * so memcpy(dst, base, serverlen) will
522 * copy up to "...git.host".
523 */
524 const char *colon_ss = strstr(base,"://");
525 if (colon_ss) {
526 serverlen = (strchr(colon_ss + 3, '/')
527 - base);
528 okay = 1;
529 }
Daniel Barkalow1b0c1e62005-09-18 14:14:19 -0400530 } else if (!memcmp(data + i, "../", 3)) {
Junio C Hamano5df1e0d2006-09-12 23:53:27 -0700531 /* Relative URL; chop the corresponding
532 * number of subpath from base (and ../
533 * from data), and concatenate the result.
534 *
535 * The code first drops ../ from data, and
536 * then drops one ../ from data and one path
537 * from base. IOW, one extra ../ is dropped
538 * from data than path is dropped from base.
539 *
540 * This is not wrong. The alternate in
541 * http://git.host/pub/scm/linux.git/
542 * to borrow from
543 * http://git.host/pub/scm/linus.git/
544 * is ../../linus.git/objects/. You need
545 * two ../../ to borrow from your direct
546 * neighbour.
547 */
Daniel Barkalow1b0c1e62005-09-18 14:14:19 -0400548 i += 3;
549 serverlen = strlen(base);
Junio C Hamano8fcf7f92006-01-31 18:15:51 -0800550 while (i + 2 < posn &&
Daniel Barkalow1b0c1e62005-09-18 14:14:19 -0400551 !memcmp(data + i, "../", 3)) {
552 do {
553 serverlen--;
554 } while (serverlen &&
555 base[serverlen - 1] != '/');
556 i += 3;
557 }
Pavel Roskina9486b02006-07-10 02:57:51 -0400558 /* If the server got removed, give up. */
Junio C Hamano8fcf7f92006-01-31 18:15:51 -0800559 okay = strchr(base, ':') - base + 3 <
Daniel Barkalow1b0c1e62005-09-18 14:14:19 -0400560 serverlen;
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800561 } else if (alt_req->http_specific) {
Daniel Barkalow1b0c1e62005-09-18 14:14:19 -0400562 char *colon = strchr(data + i, ':');
563 char *slash = strchr(data + i, '/');
564 if (colon && slash && colon < data + posn &&
565 slash < data + posn && colon < slash) {
566 okay = 1;
567 }
568 }
Junio C Hamano5df1e0d2006-09-12 23:53:27 -0700569 /* skip "objects\n" at end */
Daniel Barkalow1b0c1e62005-09-18 14:14:19 -0400570 if (okay) {
571 target = xmalloc(serverlen + posn - i - 6);
Junio C Hamano5df1e0d2006-09-12 23:53:27 -0700572 memcpy(target, base, serverlen);
573 memcpy(target + serverlen, data + i,
574 posn - i - 7);
575 target[serverlen + posn - i - 7] = 0;
Daniel Barkalowb3661562005-09-14 23:26:08 -0400576 if (get_verbosely)
Junio C Hamano8fcf7f92006-01-31 18:15:51 -0800577 fprintf(stderr,
Daniel Barkalowb3661562005-09-14 23:26:08 -0400578 "Also look at %s\n", target);
579 newalt = xmalloc(sizeof(*newalt));
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700580 newalt->next = NULL;
Daniel Barkalowb3661562005-09-14 23:26:08 -0400581 newalt->base = target;
582 newalt->got_indices = 0;
583 newalt->packs = NULL;
Nick Hengeveld8d9fbe52006-04-04 05:33:18 -0700584
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700585 while (tail->next != NULL)
586 tail = tail->next;
587 tail->next = newalt;
Daniel Barkalowb3661562005-09-14 23:26:08 -0400588 }
589 }
590 i = posn + 1;
591 }
Sergey Vlasovbc8f2652005-10-13 10:49:53 -0700592
Nick Hengeveldf7eb2902005-10-21 12:06:20 -0700593 got_alternates = 1;
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800594}
595
Petr Baudis8e29f6a2006-07-27 23:56:22 +0200596static void fetch_alternates(const char *base)
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800597{
598 struct buffer buffer;
599 char *url;
600 char *data;
601 struct active_request_slot *slot;
Nick Hengeveldcb754fd2006-01-31 18:00:37 -0800602 struct alternates_request alt_req;
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800603
604 /* If another request has already started fetching alternates,
605 wait for them to arrive and return to processing this request's
606 curl message */
Nick Hengeveld29508e12005-11-18 11:02:58 -0800607#ifdef USE_CURL_MULTI
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800608 while (got_alternates == 0) {
Nick Hengeveld29508e12005-11-18 11:02:58 -0800609 step_active_slots();
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800610 }
Nick Hengeveld29508e12005-11-18 11:02:58 -0800611#endif
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800612
613 /* Nothing to do if they've already been fetched */
614 if (got_alternates == 1)
615 return;
616
617 /* Start the fetch */
618 got_alternates = 0;
619
620 data = xmalloc(4096);
621 buffer.size = 4096;
622 buffer.posn = 0;
623 buffer.buffer = data;
624
625 if (get_verbosely)
626 fprintf(stderr, "Getting alternates list for %s\n", base);
Junio C Hamano8fcf7f92006-01-31 18:15:51 -0800627
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800628 url = xmalloc(strlen(base) + 31);
629 sprintf(url, "%s/objects/info/http-alternates", base);
630
631 /* Use a callback to process the result, since another request
632 may fail and need to have alternates loaded before continuing */
633 slot = get_active_slot();
Nick Hengevelde388ab72005-11-18 11:03:04 -0800634 slot->callback_func = process_alternates_response;
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800635 slot->callback_data = &alt_req;
636
637 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
Nick Hengeveld29508e12005-11-18 11:02:58 -0800638 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
Nick Hengeveldacc075a2005-11-12 09:11:32 -0800639 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
640
641 alt_req.base = base;
642 alt_req.url = url;
643 alt_req.buffer = &buffer;
644 alt_req.http_specific = 1;
645 alt_req.slot = slot;
646
647 if (start_active_slot(slot))
648 run_active_slot(slot);
649 else
650 got_alternates = -1;
651
652 free(data);
653 free(url);
Daniel Barkalowb3661562005-09-14 23:26:08 -0400654}
655
656static int fetch_indices(struct alt_base *repo)
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400657{
658 unsigned char sha1[20];
659 char *url;
660 struct buffer buffer;
661 char *data;
662 int i = 0;
663
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700664 struct active_request_slot *slot;
Nick Hengeveldcb754fd2006-01-31 18:00:37 -0800665 struct slot_results results;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700666
Daniel Barkalowb3661562005-09-14 23:26:08 -0400667 if (repo->got_indices)
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400668 return 0;
669
670 data = xmalloc(4096);
671 buffer.size = 4096;
672 buffer.posn = 0;
673 buffer.buffer = data;
674
675 if (get_verbosely)
Petr Baudis6fd72e32005-11-12 01:49:59 +0100676 fprintf(stderr, "Getting pack list for %s\n", repo->base);
Junio C Hamano8fcf7f92006-01-31 18:15:51 -0800677
Daniel Barkalowb3661562005-09-14 23:26:08 -0400678 url = xmalloc(strlen(repo->base) + 21);
679 sprintf(url, "%s/objects/info/packs", repo->base);
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400680
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700681 slot = get_active_slot();
Nick Hengeveldc8568e12006-01-31 11:06:55 -0800682 slot->results = &results;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700683 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
Nick Hengeveld29508e12005-11-18 11:02:58 -0800684 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700685 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
686 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
687 if (start_active_slot(slot)) {
688 run_active_slot(slot);
Nick Hengeveldc8568e12006-01-31 11:06:55 -0800689 if (results.curl_result != CURLE_OK) {
Junio C Hamanobe4a0152006-09-16 10:58:20 -0700690 if (missing_target(&results)) {
Nick Hengeveld5e3a7692005-11-18 11:03:11 -0800691 repo->got_indices = 1;
692 free(buffer.buffer);
693 return 0;
694 } else {
695 repo->got_indices = 0;
696 free(buffer.buffer);
697 return error("%s", curl_errorstr);
698 }
Sergey Vlasovbc8f2652005-10-13 10:49:53 -0700699 }
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700700 } else {
Nick Hengeveld5e3a7692005-11-18 11:03:11 -0800701 repo->got_indices = 0;
Sergey Vlasovbc8f2652005-10-13 10:49:53 -0700702 free(buffer.buffer);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700703 return error("Unable to start request");
704 }
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400705
Sergey Vlasovbc8f2652005-10-13 10:49:53 -0700706 data = buffer.buffer;
Daniel Barkalowb3661562005-09-14 23:26:08 -0400707 while (i < buffer.posn) {
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400708 switch (data[i]) {
709 case 'P':
710 i++;
Junio C Hamano455c1612005-12-21 12:10:10 -0800711 if (i + 52 <= buffer.posn &&
Junio C Hamanocc44c762007-02-20 01:53:29 -0800712 !prefixcmp(data + i, " pack-") &&
Junio C Hamano1968d772007-02-20 01:55:07 -0800713 !prefixcmp(data + i + 46, ".pack\n")) {
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400714 get_sha1_hex(data + i + 6, sha1);
Daniel Barkalowb3661562005-09-14 23:26:08 -0400715 setup_index(repo, sha1);
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400716 i += 51;
717 break;
718 }
719 default:
Junio C Hamano455c1612005-12-21 12:10:10 -0800720 while (i < buffer.posn && data[i] != '\n')
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400721 i++;
722 }
723 i++;
Daniel Barkalowb3661562005-09-14 23:26:08 -0400724 }
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400725
Sergey Vlasovbc8f2652005-10-13 10:49:53 -0700726 free(buffer.buffer);
Daniel Barkalowb3661562005-09-14 23:26:08 -0400727 repo->got_indices = 1;
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400728 return 0;
729}
730
Daniel Barkalowb3661562005-09-14 23:26:08 -0400731static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400732{
733 char *url;
734 struct packed_git *target;
735 struct packed_git **lst;
736 FILE *packfile;
737 char *filename;
Nick Hengeveld49a0f242005-09-28 10:14:04 -0700738 char tmpfile[PATH_MAX];
739 int ret;
740 long prev_posn = 0;
741 char range[RANGE_HEADER_SIZE];
742 struct curl_slist *range_header = NULL;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700743
744 struct active_request_slot *slot;
Nick Hengeveldcb754fd2006-01-31 18:00:37 -0800745 struct slot_results results;
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400746
Daniel Barkalowb3661562005-09-14 23:26:08 -0400747 if (fetch_indices(repo))
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400748 return -1;
Daniel Barkalowb3661562005-09-14 23:26:08 -0400749 target = find_sha1_pack(sha1, repo->packs);
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400750 if (!target)
Daniel Barkalowb3661562005-09-14 23:26:08 -0400751 return -1;
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400752
753 if (get_verbosely) {
754 fprintf(stderr, "Getting pack %s\n",
755 sha1_to_hex(target->sha1));
756 fprintf(stderr, " which contains %s\n",
757 sha1_to_hex(sha1));
758 }
759
Daniel Barkalowb3661562005-09-14 23:26:08 -0400760 url = xmalloc(strlen(repo->base) + 65);
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400761 sprintf(url, "%s/objects/pack/pack-%s.pack",
Daniel Barkalowb3661562005-09-14 23:26:08 -0400762 repo->base, sha1_to_hex(target->sha1));
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400763
764 filename = sha1_pack_name(target->sha1);
Nick Hengeveld49a0f242005-09-28 10:14:04 -0700765 snprintf(tmpfile, sizeof(tmpfile), "%s.temp", filename);
766 packfile = fopen(tmpfile, "a");
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400767 if (!packfile)
768 return error("Unable to open local file %s for pack",
769 filename);
770
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700771 slot = get_active_slot();
Nick Hengeveldc8568e12006-01-31 11:06:55 -0800772 slot->results = &results;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700773 curl_easy_setopt(slot->curl, CURLOPT_FILE, packfile);
774 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
775 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
Nick Hengeveld03126002005-10-10 23:22:01 -0700776 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700777 slot->local = packfile;
Nick Hengeveld1ddea772005-09-26 10:52:11 -0700778
Nick Hengeveld49a0f242005-09-28 10:14:04 -0700779 /* If there is data present from a previous transfer attempt,
780 resume where it left off */
781 prev_posn = ftell(packfile);
782 if (prev_posn>0) {
783 if (get_verbosely)
784 fprintf(stderr,
785 "Resuming fetch of pack %s at byte %ld\n",
786 sha1_to_hex(target->sha1), prev_posn);
787 sprintf(range, "Range: bytes=%ld-", prev_posn);
788 range_header = curl_slist_append(range_header, range);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700789 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, range_header);
Nick Hengeveld49a0f242005-09-28 10:14:04 -0700790 }
791
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700792 if (start_active_slot(slot)) {
793 run_active_slot(slot);
Nick Hengeveldc8568e12006-01-31 11:06:55 -0800794 if (results.curl_result != CURLE_OK) {
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700795 fclose(packfile);
796 return error("Unable to get pack file %s\n%s", url,
797 curl_errorstr);
798 }
799 } else {
Petr Baudis313c4712005-11-12 00:55:16 +0100800 fclose(packfile);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700801 return error("Unable to start request");
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400802 }
803
Shawn O. Pearce1c23d792007-01-09 20:04:52 -0500804 target->pack_size = ftell(packfile);
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400805 fclose(packfile);
806
Junio C Hamanob721e012005-10-10 23:22:01 -0700807 ret = move_temp_to_file(tmpfile, filename);
Nick Hengeveld49a0f242005-09-28 10:14:04 -0700808 if (ret)
Junio C Hamanob721e012005-10-10 23:22:01 -0700809 return ret;
Nick Hengeveld49a0f242005-09-28 10:14:04 -0700810
Daniel Barkalowb3661562005-09-14 23:26:08 -0400811 lst = &repo->packs;
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400812 while (*lst != target)
813 lst = &((*lst)->next);
814 *lst = (*lst)->next;
815
Junio C Hamano271421c2005-09-30 00:07:39 -0700816 if (verify_pack(target, 0))
817 return -1;
barkalow@iabervon.org182005b2005-07-31 20:54:17 -0400818 install_packed_git(target);
819
820 return 0;
821}
822
Mark Wooding53f31382006-02-07 10:07:39 +0000823static void abort_object_request(struct object_request *obj_req)
824{
825 if (obj_req->local >= 0) {
826 close(obj_req->local);
827 obj_req->local = -1;
828 }
829 unlink(obj_req->tmpfile);
830 if (obj_req->slot) {
Junio C Hamanoa6080a02007-06-07 00:04:01 -0700831 release_active_slot(obj_req->slot);
Mark Wooding53f31382006-02-07 10:07:39 +0000832 obj_req->slot = NULL;
833 }
834 release_object_request(obj_req);
835}
836
Peter Hagervalla7928f82005-09-28 14:04:54 +0200837static int fetch_object(struct alt_base *repo, unsigned char *sha1)
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -0700838{
839 char *hex = sha1_to_hex(sha1);
Nick Hengeveld29508e12005-11-18 11:02:58 -0800840 int ret = 0;
Nick Hengevelde388ab72005-11-18 11:03:04 -0800841 struct object_request *obj_req = object_queue_head;
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -0700842
David Rientjesa89fccd2006-08-17 11:54:57 -0700843 while (obj_req != NULL && hashcmp(obj_req->sha1, sha1))
Nick Hengevelde388ab72005-11-18 11:03:04 -0800844 obj_req = obj_req->next;
845 if (obj_req == NULL)
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700846 return error("Couldn't find request for %s in the queue", hex);
Junio C Hamano271421c2005-09-30 00:07:39 -0700847
Nick Hengevelde388ab72005-11-18 11:03:04 -0800848 if (has_sha1_file(obj_req->sha1)) {
Mark Wooding53f31382006-02-07 10:07:39 +0000849 abort_object_request(obj_req);
Nick Hengeveld11f0daf2005-10-10 23:22:01 -0700850 return 0;
851 }
852
Nick Hengevelda7a8d372005-10-10 23:22:01 -0700853#ifdef USE_CURL_MULTI
Nick Hengevelde388ab72005-11-18 11:03:04 -0800854 while (obj_req->state == WAITING) {
Nick Hengeveld29508e12005-11-18 11:02:58 -0800855 step_active_slots();
Nick Hengeveld49a0f242005-09-28 10:14:04 -0700856 }
Nick Hengevelda7a8d372005-10-10 23:22:01 -0700857#else
Nick Hengevelde388ab72005-11-18 11:03:04 -0800858 start_object_request(obj_req);
Nick Hengevelda7a8d372005-10-10 23:22:01 -0700859#endif
Nick Hengeveld49a0f242005-09-28 10:14:04 -0700860
Nick Hengevelde388ab72005-11-18 11:03:04 -0800861 while (obj_req->state == ACTIVE) {
862 run_active_slot(obj_req->slot);
Nick Hengevelda7a8d372005-10-10 23:22:01 -0700863 }
Nick Hengevelde388ab72005-11-18 11:03:04 -0800864 if (obj_req->local != -1) {
865 close(obj_req->local); obj_req->local = -1;
Petr Baudis313c4712005-11-12 00:55:16 +0100866 }
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700867
Nick Hengevelde388ab72005-11-18 11:03:04 -0800868 if (obj_req->state == ABORTED) {
Nick Hengeveld29508e12005-11-18 11:02:58 -0800869 ret = error("Request for %s aborted", hex);
Nick Hengevelde388ab72005-11-18 11:03:04 -0800870 } else if (obj_req->curl_result != CURLE_OK &&
871 obj_req->http_code != 416) {
Junio C Hamanobe4a0152006-09-16 10:58:20 -0700872 if (missing_target(obj_req))
Petr Baudise2029eb2005-10-21 18:18:46 +0200873 ret = -1; /* Be silent, it is probably in a pack. */
874 else
875 ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
Nick Hengevelde388ab72005-11-18 11:03:04 -0800876 obj_req->errorstr, obj_req->curl_result,
877 obj_req->http_code, hex);
878 } else if (obj_req->zret != Z_STREAM_END) {
Junio C Hamanobb528072006-03-20 14:07:59 -0800879 corrupt_object_found++;
Junio C Hamanobd2afde2006-02-22 17:47:10 -0800880 ret = error("File %s (%s) corrupt", hex, obj_req->url);
David Rientjesa89fccd2006-08-17 11:54:57 -0700881 } else if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
Junio C Hamanobd2afde2006-02-22 17:47:10 -0800882 ret = error("File %s has bad hash", hex);
Nick Hengevelde388ab72005-11-18 11:03:04 -0800883 } else if (obj_req->rename < 0) {
Mark Wooding7b934ec2006-02-01 11:44:35 +0000884 ret = error("unable to write sha1 filename %s",
885 obj_req->filename);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700886 }
887
Nick Hengevelde388ab72005-11-18 11:03:04 -0800888 release_object_request(obj_req);
Nick Hengeveld29508e12005-11-18 11:02:58 -0800889 return ret;
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -0700890}
891
Daniel Barkalowb3661562005-09-14 23:26:08 -0400892int fetch(unsigned char *sha1)
893{
894 struct alt_base *altbase = alt;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700895
896 if (!fetch_object(altbase, sha1))
897 return 0;
Daniel Barkalowb3661562005-09-14 23:26:08 -0400898 while (altbase) {
Daniel Barkalowb3661562005-09-14 23:26:08 -0400899 if (!fetch_pack(altbase, sha1))
900 return 0;
Nick Hengeveldf7eb2902005-10-21 12:06:20 -0700901 fetch_alternates(alt->base);
Daniel Barkalowb3661562005-09-14 23:26:08 -0400902 altbase = altbase->next;
903 }
Junio C Hamanobd2afde2006-02-22 17:47:10 -0800904 return error("Unable to find %s under %s", sha1_to_hex(sha1),
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700905 alt->base);
Daniel Barkalowb3661562005-09-14 23:26:08 -0400906}
907
Junio C Hamano94fa4472005-10-10 23:22:02 -0700908static inline int needs_quote(int ch)
909{
Florian Forstercfd432e62006-06-18 17:18:03 +0200910 if (((ch >= 'A') && (ch <= 'Z'))
911 || ((ch >= 'a') && (ch <= 'z'))
912 || ((ch >= '0') && (ch <= '9'))
913 || (ch == '/')
914 || (ch == '-')
915 || (ch == '.'))
Junio C Hamano94fa4472005-10-10 23:22:02 -0700916 return 0;
Florian Forstercfd432e62006-06-18 17:18:03 +0200917 return 1;
Junio C Hamano94fa4472005-10-10 23:22:02 -0700918}
919
920static inline int hex(int v)
921{
922 if (v < 10) return '0' + v;
923 else return 'A' + v - 10;
924}
925
926static char *quote_ref_url(const char *base, const char *ref)
927{
928 const char *cp;
929 char *dp, *qref;
930 int len, baselen, ch;
931
932 baselen = strlen(base);
Gerrit Pape2afea3b2007-03-28 09:46:15 +0000933 len = baselen + 7; /* "/refs/" + NUL */
Junio C Hamano94fa4472005-10-10 23:22:02 -0700934 for (cp = ref; (ch = *cp) != 0; cp++, len++)
935 if (needs_quote(ch))
936 len += 2; /* extra two hex plus replacement % */
937 qref = xmalloc(len);
938 memcpy(qref, base, baselen);
Gerrit Pape2afea3b2007-03-28 09:46:15 +0000939 memcpy(qref + baselen, "/refs/", 6);
940 for (cp = ref, dp = qref + baselen + 6; (ch = *cp) != 0; cp++) {
Junio C Hamano94fa4472005-10-10 23:22:02 -0700941 if (needs_quote(ch)) {
942 *dp++ = '%';
943 *dp++ = hex((ch >> 4) & 0xF);
944 *dp++ = hex(ch & 0xF);
945 }
946 else
947 *dp++ = ch;
948 }
949 *dp = 0;
950
951 return qref;
952}
953
Daniel Barkalowcd541a62005-06-06 16:38:26 -0400954int fetch_ref(char *ref, unsigned char *sha1)
955{
Junio C Hamano94fa4472005-10-10 23:22:02 -0700956 char *url;
Daniel Barkalowfa3e0652005-06-21 20:45:49 -0400957 char hex[42];
958 struct buffer buffer;
Petr Baudis8e29f6a2006-07-27 23:56:22 +0200959 const char *base = alt->base;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700960 struct active_request_slot *slot;
Nick Hengeveldcb754fd2006-01-31 18:00:37 -0800961 struct slot_results results;
Daniel Barkalowfa3e0652005-06-21 20:45:49 -0400962 buffer.size = 41;
963 buffer.posn = 0;
964 buffer.buffer = hex;
965 hex[41] = '\0';
Junio C Hamano8fcf7f92006-01-31 18:15:51 -0800966
Junio C Hamano94fa4472005-10-10 23:22:02 -0700967 url = quote_ref_url(base, ref);
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700968 slot = get_active_slot();
Nick Hengeveldc8568e12006-01-31 11:06:55 -0800969 slot->results = &results;
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700970 curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
971 curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
972 curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
973 curl_easy_setopt(slot->curl, CURLOPT_URL, url);
974 if (start_active_slot(slot)) {
975 run_active_slot(slot);
Nick Hengeveldc8568e12006-01-31 11:06:55 -0800976 if (results.curl_result != CURLE_OK)
Nick Hengeveld1d389ab2005-10-10 23:22:01 -0700977 return error("Couldn't get %s for %s\n%s",
978 url, ref, curl_errorstr);
979 } else {
980 return error("Unable to start request");
981 }
Daniel Barkalowfa3e0652005-06-21 20:45:49 -0400982
983 hex[40] = '\0';
984 get_sha1_hex(hex, sha1);
985 return 0;
Daniel Barkalowcd541a62005-06-06 16:38:26 -0400986}
987
Petr Baudis8e29f6a2006-07-27 23:56:22 +0200988int main(int argc, const char **argv)
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -0700989{
Petr Baudis8e29f6a2006-07-27 23:56:22 +0200990 int commits;
991 const char **write_ref = NULL;
992 char **commit_id;
993 const char *url;
Gerrit Pape9c880b32007-03-28 09:47:35 +0000994 char *s;
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -0700995 int arg = 1;
Nick Hengeveld7b9ae532005-10-21 12:06:27 -0700996 int rc = 0;
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -0700997
Junio C Hamano5a327712005-11-26 00:47:59 -0800998 setup_git_directory();
Shawn Pearced0740d92006-05-19 03:29:26 -0400999 git_config(git_default_config);
Junio C Hamano5a327712005-11-26 00:47:59 -08001000
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -07001001 while (arg < argc && argv[arg][0] == '-') {
1002 if (argv[arg][1] == 't') {
Daniel Barkalow4250a5e2005-04-30 16:53:56 -07001003 get_tree = 1;
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -07001004 } else if (argv[arg][1] == 'c') {
Daniel Barkalow4250a5e2005-04-30 16:53:56 -07001005 get_history = 1;
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -07001006 } else if (argv[arg][1] == 'a') {
Daniel Barkalow4250a5e2005-04-30 16:53:56 -07001007 get_all = 1;
1008 get_tree = 1;
1009 get_history = 1;
Junio C Hamanoe78d9772005-05-06 01:37:21 -07001010 } else if (argv[arg][1] == 'v') {
1011 get_verbosely = 1;
Daniel Barkalowfa3e0652005-06-21 20:45:49 -04001012 } else if (argv[arg][1] == 'w') {
Petr Baudis8e29f6a2006-07-27 23:56:22 +02001013 write_ref = &argv[arg + 1];
Daniel Barkalowfa3e0652005-06-21 20:45:49 -04001014 arg++;
Daniel Barkalow820eca62005-09-26 21:38:08 -04001015 } else if (!strcmp(argv[arg], "--recover")) {
1016 get_recover = 1;
Petr Baudis8e29f6a2006-07-27 23:56:22 +02001017 } else if (!strcmp(argv[arg], "--stdin")) {
1018 commits_on_stdin = 1;
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -07001019 }
1020 arg++;
1021 }
Petr Baudis8e29f6a2006-07-27 23:56:22 +02001022 if (argc < arg + 2 - commits_on_stdin) {
1023 usage("git-http-fetch [-c] [-t] [-a] [-v] [--recover] [-w ref] [--stdin] commit-id url");
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -07001024 return 1;
1025 }
Petr Baudis8e29f6a2006-07-27 23:56:22 +02001026 if (commits_on_stdin) {
1027 commits = pull_targets_stdin(&commit_id, &write_ref);
1028 } else {
1029 commit_id = (char **) &argv[arg++];
1030 commits = 1;
1031 }
1032 url = argv[arg];
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -07001033
Nick Hengeveld29508e12005-11-18 11:02:58 -08001034 http_init();
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -07001035
Sergey Vlasov1db69b52005-09-13 19:38:58 +04001036 no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
Darrin Thompson3dcb90f2005-07-12 21:12:40 -05001037
Daniel Barkalowb3661562005-09-14 23:26:08 -04001038 alt = xmalloc(sizeof(*alt));
Gerrit Pape2afea3b2007-03-28 09:46:15 +00001039 alt->base = xmalloc(strlen(url) + 1);
1040 strcpy(alt->base, url);
Gerrit Pape9c880b32007-03-28 09:47:35 +00001041 for (s = alt->base + strlen(alt->base) - 1; *s == '/'; --s)
1042 *s = 0;
Daniel Barkalowb3661562005-09-14 23:26:08 -04001043 alt->got_indices = 0;
1044 alt->packs = NULL;
1045 alt->next = NULL;
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -07001046
Petr Baudis8e29f6a2006-07-27 23:56:22 +02001047 if (pull(commits, commit_id, write_ref, url))
Nick Hengeveld7b9ae532005-10-21 12:06:27 -07001048 rc = 1;
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -07001049
Nick Hengeveld29508e12005-11-18 11:02:58 -08001050 http_cleanup();
1051
Sean07001f92006-05-20 18:46:33 -04001052 curl_slist_free_all(no_pragma_header);
1053
Petr Baudis8e29f6a2006-07-27 23:56:22 +02001054 if (commits_on_stdin)
1055 pull_targets_free(commits, commit_id, write_ref);
1056
Junio C Hamanobb528072006-03-20 14:07:59 -08001057 if (corrupt_object_found) {
1058 fprintf(stderr,
1059"Some loose object were found to be corrupt, but they might be just\n"
1060"a false '404 Not Found' error message sent with incorrect HTTP\n"
Junio C Hamanodf391b12007-01-28 16:33:58 -08001061"status code. Suggest running git-fsck.\n");
Junio C Hamanobb528072006-03-20 14:07:59 -08001062 }
Nick Hengeveld7b9ae532005-10-21 12:06:27 -07001063 return rc;
Daniel Barkalow6eb7ed52005-04-23 18:47:23 -07001064}