3
#include "walker.h"
4
#include "http.h"
5
6
-#define PREV_BUF_SIZE 4096
7
-
6
struct alt_base
7
{
8
char *base;
23
struct walker *walker;
24
unsigned char sha1[20];
25
struct alt_base *repo;
28
- char *url;
29
- char filename[PATH_MAX];
30
- char tmpfile[PATH_MAX];
31
- int local;
26
enum object_request_state state;
33
- CURLcode curl_result;
34
- char errorstr[CURL_ERROR_SIZE];
35
- long http_code;
36
- unsigned char real_sha1[20];
37
- git_SHA_CTX c;
38
- z_stream stream;
39
- int zret;
40
- int rename;
41
- struct active_request_slot *slot;
27
+ struct http_object_request *req;
28
struct object_request *next;
29
};
30
45
46
static struct object_request *object_queue_head;
47
62
-static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
63
- void *data)
64
-{
65
- unsigned char expn[4096];
66
- size_t size = eltsize * nmemb;
67
- int posn = 0;
68
- struct object_request *obj_req = (struct object_request *)data;
69
- do {
70
- ssize_t retval = xwrite(obj_req->local,
71
- (char *) ptr + posn, size - posn);
72
- if (retval < 0)
73
- return posn;
74
- posn += retval;
75
- } while (posn < size);
76
-
77
- obj_req->stream.avail_in = size;
78
- obj_req->stream.next_in = ptr;
79
- do {
80
- obj_req->stream.next_out = expn;
81
- obj_req->stream.avail_out = sizeof(expn);
82
- obj_req->zret = git_inflate(&obj_req->stream, Z_SYNC_FLUSH);
83
- git_SHA1_Update(&obj_req->c, expn,
84
- sizeof(expn) - obj_req->stream.avail_out);
85
- } while (obj_req->stream.avail_in && obj_req->zret == Z_OK);
86
- data_received++;
87
- return size;
88
-}
89
-
48
static void fetch_alternates(struct walker *walker, const char *base);
49
50
static void process_object_response(void *callback_data);
52
static void start_object_request(struct walker *walker,
53
struct object_request *obj_req)
54
{
97
- char *hex = sha1_to_hex(obj_req->sha1);
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;
55
struct active_request_slot *slot;
56
+ struct http_object_request *req;
57
109
- snprintf(prevfile, sizeof(prevfile), "%s.prev", obj_req->filename);
110
- unlink_or_warn(prevfile);
111
- rename(obj_req->tmpfile, prevfile);
112
- unlink_or_warn(obj_req->tmpfile);
113
-
114
- if (obj_req->local != -1)
115
- error("fd leakage in start: %d", obj_req->local);
116
- obj_req->local = open(obj_req->tmpfile,
117
- O_WRONLY | O_CREAT | O_EXCL, 0666);
118
- /*
119
- * This could have failed due to the "lazy directory creation";
120
- * try to mkdir the last path component.
121
- */
122
- if (obj_req->local < 0 && errno == ENOENT) {
123
- char *dir = strrchr(obj_req->tmpfile, '/');
124
- if (dir) {
125
- *dir = 0;
126
- mkdir(obj_req->tmpfile, 0777);
127
- *dir = '/';
128
- }
129
- obj_req->local = open(obj_req->tmpfile,
130
- O_WRONLY | O_CREAT | O_EXCL, 0666);
131
- }
132
-
133
- if (obj_req->local < 0) {
58
+ req = new_http_object_request(obj_req->repo->base, obj_req->sha1);
59
+ if (req == NULL) {
60
obj_req->state = ABORTED;
135
- error("Couldn't create temporary file %s for %s: %s",
136
- obj_req->tmpfile, obj_req->filename, strerror(errno));
61
return;
62
}
63
+ obj_req->req = req;
64
140
- memset(&obj_req->stream, 0, sizeof(obj_req->stream));
141
-
142
- git_inflate_init(&obj_req->stream);
143
-
144
- git_SHA1_Init(&obj_req->c);
145
-
146
- url = xmalloc(strlen(obj_req->repo->base) + 51);
147
- obj_req->url = xmalloc(strlen(obj_req->repo->base) + 51);
148
- strcpy(url, obj_req->repo->base);
149
- posn = url + strlen(obj_req->repo->base);
150
- strcpy(posn, "/objects/");
151
- posn += 9;
152
- memcpy(posn, hex, 2);
153
- posn += 2;
154
- *(posn++) = '/';
155
- strcpy(posn, hex + 2);
156
- strcpy(obj_req->url, url);
157
-
158
- /*
159
- * If a previous temp file is present, process what was already
160
- * fetched.
161
- */
162
- prevlocal = open(prevfile, O_RDONLY);
163
- if (prevlocal != -1) {
164
- do {
165
- prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
166
- if (prev_read>0) {
167
- if (fwrite_sha1_file(prev_buf,
168
- 1,
169
- prev_read,
170
- obj_req) == prev_read)
171
- prev_posn += prev_read;
172
- else
173
- prev_read = -1;
174
- }
175
- } while (prev_read > 0);
176
- close(prevlocal);
177
- }
178
- unlink_or_warn(prevfile);
179
-
180
- /*
181
- * Reset inflate/SHA1 if there was an error reading the previous temp
182
- * file; also rewind to the beginning of the local file.
183
- */
184
- if (prev_read == -1) {
185
- memset(&obj_req->stream, 0, sizeof(obj_req->stream));
186
- git_inflate_init(&obj_req->stream);
187
- git_SHA1_Init(&obj_req->c);
188
- if (prev_posn>0) {
189
- prev_posn = 0;
190
- lseek(obj_req->local, 0, SEEK_SET);
191
- ftruncate(obj_req->local, 0);
192
- }
193
- }
194
-
195
- slot = get_active_slot();
65
+ slot = req->slot;
66
slot->callback_func = process_object_response;
67
slot->callback_data = obj_req;
198
- obj_req->slot = slot;
199
-
200
- curl_easy_setopt(slot->curl, CURLOPT_FILE, obj_req);
201
- curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
202
- curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, obj_req->errorstr);
203
- curl_easy_setopt(slot->curl, CURLOPT_URL, url);
204
- curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
205
-
206
- /*
207
- * If we have successfully processed data from a previous fetch
208
- * attempt, only fetch the data we don't already have.
209
- */
210
- if (prev_posn>0) {
211
- if (walker->get_verbosely)
212
- fprintf(stderr,
213
- "Resuming fetch of object %s at byte %ld\n",
214
- hex, prev_posn);
215
- sprintf(range, "Range: bytes=%ld-", prev_posn);
216
- range_header = curl_slist_append(range_header, range);
217
- curl_easy_setopt(slot->curl,
218
- CURLOPT_HTTPHEADER, range_header);
219
- }
68
69
/* Try to get the request started, abort the request on error */
70
obj_req->state = ACTIVE;
71
if (!start_active_slot(slot)) {
72
obj_req->state = ABORTED;
225
- obj_req->slot = NULL;
226
- close(obj_req->local);
227
- obj_req->local = -1;
228
- free(obj_req->url);
73
+ release_http_object_request(req);
74
return;
75
}
76
}
77
78
static void finish_object_request(struct object_request *obj_req)
79
{
235
- struct stat st;
236
-
237
- close(obj_req->local);
238
- obj_req->local = -1;
239
-
240
- if (obj_req->http_code == 416) {
241
- fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
242
- } else if (obj_req->curl_result != CURLE_OK) {
243
- if (stat(obj_req->tmpfile, &st) == 0)
244
- if (st.st_size == 0)
245
- unlink_or_warn(obj_req->tmpfile);
80
+ if (finish_http_object_request(obj_req->req))
81
return;
247
- }
248
-
249
- git_inflate_end(&obj_req->stream);
250
- git_SHA1_Final(obj_req->real_sha1, &obj_req->c);
251
- if (obj_req->zret != Z_STREAM_END) {
252
- unlink_or_warn(obj_req->tmpfile);
253
- return;
254
- }
255
- if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
256
- unlink_or_warn(obj_req->tmpfile);
257
- return;
258
- }
259
- obj_req->rename =
260
- move_temp_to_file(obj_req->tmpfile, obj_req->filename);
82
262
- if (obj_req->rename == 0)
83
+ if (obj_req->req->rename == 0)
84
walker_say(obj_req->walker, "got %s\n", sha1_to_hex(obj_req->sha1));
85
}
86
92
struct walker_data *data = walker->data;
93
struct alt_base *alt = data->alt;
94
274
- obj_req->curl_result = obj_req->slot->curl_result;
275
- obj_req->http_code = obj_req->slot->http_code;
276
- obj_req->slot = NULL;
95
+ process_http_object_request(obj_req->req);
96
obj_req->state = COMPLETE;
97
98
/* Use alternates if necessary */
280
- if (missing_target(obj_req)) {
99
+ if (missing_target(obj_req->req)) {
100
fetch_alternates(walker, alt->base);
101
if (obj_req->repo->next != NULL) {
102
obj_req->repo =
103
obj_req->repo->next;
285
- close(obj_req->local);
286
- obj_req->local = -1;
104
+ release_http_object_request(obj_req->req);
105
start_object_request(walker, obj_req);
106
return;
107
}
114
{
115
struct object_request *entry = object_queue_head;
116
299
- if (obj_req->local != -1)
300
- error("fd leakage in release: %d", obj_req->local);
117
+ if (obj_req->req !=NULL && obj_req->req->localfile != -1)
118
+ error("fd leakage in release: %d", obj_req->req->localfile);
119
if (obj_req == object_queue_head) {
120
object_queue_head = obj_req->next;
121
} else {
125
entry->next = entry->next->next;
126
}
127
310
- free(obj_req->url);
128
free(obj_req);
129
}
130
152
struct object_request *newreq;
153
struct object_request *tail;
154
struct walker_data *data = walker->data;
338
- char *filename = sha1_file_name(sha1);
155
156
newreq = xmalloc(sizeof(*newreq));
157
newreq->walker = walker;
158
hashcpy(newreq->sha1, sha1);
159
newreq->repo = data->alt;
344
- newreq->url = NULL;
345
- newreq->local = -1;
160
newreq->state = WAITING;
347
- snprintf(newreq->filename, sizeof(newreq->filename), "%s", filename);
348
- snprintf(newreq->tmpfile, sizeof(newreq->tmpfile),
349
- "%s.temp", filename);
350
- newreq->slot = NULL;
161
+ newreq->req = NULL;
162
newreq->next = NULL;
163
164
http_is_verbose = walker->get_verbosely;
449
450
static void abort_object_request(struct object_request *obj_req)
451
{
641
- if (obj_req->local >= 0) {
642
- close(obj_req->local);
643
- obj_req->local = -1;
644
- }
645
- unlink_or_warn(obj_req->tmpfile);
646
- if (obj_req->slot) {
647
- release_active_slot(obj_req->slot);
648
- obj_req->slot = NULL;
649
- }
452
release_object_request(obj_req);
453
}
454
457
char *hex = sha1_to_hex(sha1);
458
int ret = 0;
459
struct object_request *obj_req = object_queue_head;
460
+ struct http_object_request *req;
461
462
while (obj_req != NULL && hashcmp(obj_req->sha1, sha1))
463
obj_req = obj_req->next;
465
return error("Couldn't find request for %s in the queue", hex);
466
467
if (has_sha1_file(obj_req->sha1)) {
468
+ if (obj_req->req != NULL)
469
+ abort_http_object_request(obj_req->req);
470
abort_object_request(obj_req);
471
return 0;
472
}
478
start_object_request(walker, obj_req);
479
#endif
480
481
+ /*
482
+ * obj_req->req might change when fetching alternates in the callback
483
+ * process_object_response; therefore, the "shortcut" variable, req,
484
+ * is used only after we're done with slots.
485
+ */
486
while (obj_req->state == ACTIVE)
677
- run_active_slot(obj_req->slot);
487
+ run_active_slot(obj_req->req->slot);
488
+
489
+ req = obj_req->req;
490
679
- if (obj_req->local != -1) {
680
- close(obj_req->local);
681
- obj_req->local = -1;
491
+ if (req->localfile != -1) {
492
+ close(req->localfile);
493
+ req->localfile = -1;
494
}
495
496
if (obj_req->state == ABORTED) {
497
ret = error("Request for %s aborted", hex);
686
- } else if (obj_req->curl_result != CURLE_OK &&
687
- obj_req->http_code != 416) {
688
- if (missing_target(obj_req))
498
+ } else if (req->curl_result != CURLE_OK &&
499
+ req->http_code != 416) {
500
+ if (missing_target(req))
501
ret = -1; /* Be silent, it is probably in a pack. */
502
else
503
ret = error("%s (curl_result = %d, http_code = %ld, sha1 = %s)",
692
- obj_req->errorstr, obj_req->curl_result,
693
- obj_req->http_code, hex);
694
- } else if (obj_req->zret != Z_STREAM_END) {
504
+ req->errorstr, req->curl_result,
505
+ req->http_code, hex);
506
+ } else if (req->zret != Z_STREAM_END) {
507
walker->corrupt_object_found++;
696
- ret = error("File %s (%s) corrupt", hex, obj_req->url);
697
- } else if (hashcmp(obj_req->sha1, obj_req->real_sha1)) {
508
+ ret = error("File %s (%s) corrupt", hex, req->url);
509
+ } else if (hashcmp(obj_req->sha1, req->real_sha1)) {
510
ret = error("File %s has bad hash", hex);
699
- } else if (obj_req->rename < 0) {
511
+ } else if (req->rename < 0) {
512
ret = error("unable to write sha1 filename %s",
701
- obj_req->filename);
513
+ req->filename);
514
}
515
516
+ release_http_object_request(req);
517
release_object_request(obj_req);
518
return ret;
519
}