Merge remote-tracking branch 'sstabellini/xen-fixes-20120801' into staging
[qemu/opensuse.git] / block / curl.c
blobe7c3634d354558ea6916eca3d6edddf020ba586f
1 /*
2 * QEMU Block driver for CURL images
4 * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
24 #include "qemu-common.h"
25 #include "block_int.h"
26 #include <curl/curl.h>
28 // #define DEBUG
29 // #define DEBUG_VERBOSE
31 #ifdef DEBUG_CURL
32 #define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0)
33 #else
34 #define DPRINTF(fmt, ...) do { } while (0)
35 #endif
37 #define CURL_NUM_STATES 8
38 #define CURL_NUM_ACB 8
39 #define SECTOR_SIZE 512
40 #define READ_AHEAD_SIZE (256 * 1024)
42 #define FIND_RET_NONE 0
43 #define FIND_RET_OK 1
44 #define FIND_RET_WAIT 2
46 struct BDRVCURLState;
48 typedef struct CURLAIOCB {
49 BlockDriverAIOCB common;
50 QEMUBH *bh;
51 QEMUIOVector *qiov;
53 int64_t sector_num;
54 int nb_sectors;
56 size_t start;
57 size_t end;
58 } CURLAIOCB;
60 typedef struct CURLState
62 struct BDRVCURLState *s;
63 CURLAIOCB *acb[CURL_NUM_ACB];
64 CURL *curl;
65 char *orig_buf;
66 size_t buf_start;
67 size_t buf_off;
68 size_t buf_len;
69 char range[128];
70 char errmsg[CURL_ERROR_SIZE];
71 char in_use;
72 } CURLState;
74 typedef struct BDRVCURLState {
75 CURLM *multi;
76 size_t len;
77 CURLState states[CURL_NUM_STATES];
78 char *url;
79 size_t readahead_size;
80 } BDRVCURLState;
82 static void curl_clean_state(CURLState *s);
83 static void curl_multi_do(void *arg);
84 static int curl_aio_flush(void *opaque);
86 static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
87 void *s, void *sp)
89 DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd);
90 switch (action) {
91 case CURL_POLL_IN:
92 qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, curl_aio_flush, s);
93 break;
94 case CURL_POLL_OUT:
95 qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, curl_aio_flush, s);
96 break;
97 case CURL_POLL_INOUT:
98 qemu_aio_set_fd_handler(fd, curl_multi_do, curl_multi_do,
99 curl_aio_flush, s);
100 break;
101 case CURL_POLL_REMOVE:
102 qemu_aio_set_fd_handler(fd, NULL, NULL, NULL, NULL);
103 break;
106 return 0;
109 static size_t curl_size_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
111 CURLState *s = ((CURLState*)opaque);
112 size_t realsize = size * nmemb;
113 size_t fsize;
115 if(sscanf(ptr, "Content-Length: %zd", &fsize) == 1) {
116 s->s->len = fsize;
119 return realsize;
122 static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
124 CURLState *s = ((CURLState*)opaque);
125 size_t realsize = size * nmemb;
126 int i;
128 DPRINTF("CURL: Just reading %zd bytes\n", realsize);
130 if (!s || !s->orig_buf)
131 goto read_end;
133 memcpy(s->orig_buf + s->buf_off, ptr, realsize);
134 s->buf_off += realsize;
136 for(i=0; i<CURL_NUM_ACB; i++) {
137 CURLAIOCB *acb = s->acb[i];
139 if (!acb)
140 continue;
142 if ((s->buf_off >= acb->end)) {
143 qemu_iovec_from_buf(acb->qiov, 0, s->orig_buf + acb->start,
144 acb->end - acb->start);
145 acb->common.cb(acb->common.opaque, 0);
146 qemu_aio_release(acb);
147 s->acb[i] = NULL;
151 read_end:
152 return realsize;
155 static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
156 CURLAIOCB *acb)
158 int i;
159 size_t end = start + len;
161 for (i=0; i<CURL_NUM_STATES; i++) {
162 CURLState *state = &s->states[i];
163 size_t buf_end = (state->buf_start + state->buf_off);
164 size_t buf_fend = (state->buf_start + state->buf_len);
166 if (!state->orig_buf)
167 continue;
168 if (!state->buf_off)
169 continue;
171 // Does the existing buffer cover our section?
172 if ((start >= state->buf_start) &&
173 (start <= buf_end) &&
174 (end >= state->buf_start) &&
175 (end <= buf_end))
177 char *buf = state->orig_buf + (start - state->buf_start);
179 qemu_iovec_from_buf(acb->qiov, 0, buf, len);
180 acb->common.cb(acb->common.opaque, 0);
182 return FIND_RET_OK;
185 // Wait for unfinished chunks
186 if ((start >= state->buf_start) &&
187 (start <= buf_fend) &&
188 (end >= state->buf_start) &&
189 (end <= buf_fend))
191 int j;
193 acb->start = start - state->buf_start;
194 acb->end = acb->start + len;
196 for (j=0; j<CURL_NUM_ACB; j++) {
197 if (!state->acb[j]) {
198 state->acb[j] = acb;
199 return FIND_RET_WAIT;
205 return FIND_RET_NONE;
208 static void curl_multi_do(void *arg)
210 BDRVCURLState *s = (BDRVCURLState *)arg;
211 int running;
212 int r;
213 int msgs_in_queue;
215 if (!s->multi)
216 return;
218 do {
219 r = curl_multi_socket_all(s->multi, &running);
220 } while(r == CURLM_CALL_MULTI_PERFORM);
222 /* Try to find done transfers, so we can free the easy
223 * handle again. */
224 do {
225 CURLMsg *msg;
226 msg = curl_multi_info_read(s->multi, &msgs_in_queue);
228 if (!msg)
229 break;
230 if (msg->msg == CURLMSG_NONE)
231 break;
233 switch (msg->msg) {
234 case CURLMSG_DONE:
236 CURLState *state = NULL;
237 curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char**)&state);
239 /* ACBs for successful messages get completed in curl_read_cb */
240 if (msg->data.result != CURLE_OK) {
241 int i;
242 for (i = 0; i < CURL_NUM_ACB; i++) {
243 CURLAIOCB *acb = state->acb[i];
245 if (acb == NULL) {
246 continue;
249 acb->common.cb(acb->common.opaque, -EIO);
250 qemu_aio_release(acb);
251 state->acb[i] = NULL;
255 curl_clean_state(state);
256 break;
258 default:
259 msgs_in_queue = 0;
260 break;
262 } while(msgs_in_queue);
265 static CURLState *curl_init_state(BDRVCURLState *s)
267 CURLState *state = NULL;
268 int i, j;
270 do {
271 for (i=0; i<CURL_NUM_STATES; i++) {
272 for (j=0; j<CURL_NUM_ACB; j++)
273 if (s->states[i].acb[j])
274 continue;
275 if (s->states[i].in_use)
276 continue;
278 state = &s->states[i];
279 state->in_use = 1;
280 break;
282 if (!state) {
283 g_usleep(100);
284 curl_multi_do(s);
286 } while(!state);
288 if (state->curl)
289 goto has_curl;
291 state->curl = curl_easy_init();
292 if (!state->curl)
293 return NULL;
294 curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
295 curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5);
296 curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
297 curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state);
298 curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state);
299 curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1);
300 curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1);
301 curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1);
302 curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
303 curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1);
305 #ifdef DEBUG_VERBOSE
306 curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1);
307 #endif
309 has_curl:
311 state->s = s;
313 return state;
316 static void curl_clean_state(CURLState *s)
318 if (s->s->multi)
319 curl_multi_remove_handle(s->s->multi, s->curl);
320 s->in_use = 0;
323 static int curl_open(BlockDriverState *bs, const char *filename, int flags)
325 BDRVCURLState *s = bs->opaque;
326 CURLState *state = NULL;
327 double d;
329 #define RA_OPTSTR ":readahead="
330 char *file;
331 char *ra;
332 const char *ra_val;
333 int parse_state = 0;
335 static int inited = 0;
337 file = g_strdup(filename);
338 s->readahead_size = READ_AHEAD_SIZE;
340 /* Parse a trailing ":readahead=#:" param, if present. */
341 ra = file + strlen(file) - 1;
342 while (ra >= file) {
343 if (parse_state == 0) {
344 if (*ra == ':')
345 parse_state++;
346 else
347 break;
348 } else if (parse_state == 1) {
349 if (*ra > '9' || *ra < '0') {
350 char *opt_start = ra - strlen(RA_OPTSTR) + 1;
351 if (opt_start > file &&
352 strncmp(opt_start, RA_OPTSTR, strlen(RA_OPTSTR)) == 0) {
353 ra_val = ra + 1;
354 ra -= strlen(RA_OPTSTR) - 1;
355 *ra = '\0';
356 s->readahead_size = atoi(ra_val);
357 break;
358 } else {
359 break;
363 ra--;
366 if ((s->readahead_size & 0x1ff) != 0) {
367 fprintf(stderr, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512\n",
368 s->readahead_size);
369 goto out_noclean;
372 if (!inited) {
373 curl_global_init(CURL_GLOBAL_ALL);
374 inited = 1;
377 DPRINTF("CURL: Opening %s\n", file);
378 s->url = file;
379 state = curl_init_state(s);
380 if (!state)
381 goto out_noclean;
383 // Get file size
385 curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1);
386 curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_size_cb);
387 if (curl_easy_perform(state->curl))
388 goto out;
389 curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
390 curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
391 curl_easy_setopt(state->curl, CURLOPT_NOBODY, 0);
392 if (d)
393 s->len = (size_t)d;
394 else if(!s->len)
395 goto out;
396 DPRINTF("CURL: Size = %zd\n", s->len);
398 curl_clean_state(state);
399 curl_easy_cleanup(state->curl);
400 state->curl = NULL;
402 // Now we know the file exists and its size, so let's
403 // initialize the multi interface!
405 s->multi = curl_multi_init();
406 curl_multi_setopt( s->multi, CURLMOPT_SOCKETDATA, s);
407 curl_multi_setopt( s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb );
408 curl_multi_do(s);
410 return 0;
412 out:
413 fprintf(stderr, "CURL: Error opening file: %s\n", state->errmsg);
414 curl_easy_cleanup(state->curl);
415 state->curl = NULL;
416 out_noclean:
417 g_free(file);
418 return -EINVAL;
421 static int curl_aio_flush(void *opaque)
423 BDRVCURLState *s = opaque;
424 int i, j;
426 for (i=0; i < CURL_NUM_STATES; i++) {
427 for(j=0; j < CURL_NUM_ACB; j++) {
428 if (s->states[i].acb[j]) {
429 return 1;
433 return 0;
436 static void curl_aio_cancel(BlockDriverAIOCB *blockacb)
438 // Do we have to implement canceling? Seems to work without...
441 static AIOPool curl_aio_pool = {
442 .aiocb_size = sizeof(CURLAIOCB),
443 .cancel = curl_aio_cancel,
447 static void curl_readv_bh_cb(void *p)
449 CURLState *state;
451 CURLAIOCB *acb = p;
452 BDRVCURLState *s = acb->common.bs->opaque;
454 qemu_bh_delete(acb->bh);
455 acb->bh = NULL;
457 size_t start = acb->sector_num * SECTOR_SIZE;
458 size_t end;
460 // In case we have the requested data already (e.g. read-ahead),
461 // we can just call the callback and be done.
462 switch (curl_find_buf(s, start, acb->nb_sectors * SECTOR_SIZE, acb)) {
463 case FIND_RET_OK:
464 qemu_aio_release(acb);
465 // fall through
466 case FIND_RET_WAIT:
467 return;
468 default:
469 break;
472 // No cache found, so let's start a new request
473 state = curl_init_state(s);
474 if (!state) {
475 acb->common.cb(acb->common.opaque, -EIO);
476 qemu_aio_release(acb);
477 return;
480 acb->start = 0;
481 acb->end = (acb->nb_sectors * SECTOR_SIZE);
483 state->buf_off = 0;
484 if (state->orig_buf)
485 g_free(state->orig_buf);
486 state->buf_start = start;
487 state->buf_len = acb->end + s->readahead_size;
488 end = MIN(start + state->buf_len, s->len) - 1;
489 state->orig_buf = g_malloc(state->buf_len);
490 state->acb[0] = acb;
492 snprintf(state->range, 127, "%zd-%zd", start, end);
493 DPRINTF("CURL (AIO): Reading %d at %zd (%s)\n",
494 (acb->nb_sectors * SECTOR_SIZE), start, state->range);
495 curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range);
497 curl_multi_add_handle(s->multi, state->curl);
498 curl_multi_do(s);
502 static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs,
503 int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
504 BlockDriverCompletionFunc *cb, void *opaque)
506 CURLAIOCB *acb;
508 acb = qemu_aio_get(&curl_aio_pool, bs, cb, opaque);
510 acb->qiov = qiov;
511 acb->sector_num = sector_num;
512 acb->nb_sectors = nb_sectors;
514 acb->bh = qemu_bh_new(curl_readv_bh_cb, acb);
516 if (!acb->bh) {
517 DPRINTF("CURL: qemu_bh_new failed\n");
518 return NULL;
521 qemu_bh_schedule(acb->bh);
522 return &acb->common;
525 static void curl_close(BlockDriverState *bs)
527 BDRVCURLState *s = bs->opaque;
528 int i;
530 DPRINTF("CURL: Close\n");
531 for (i=0; i<CURL_NUM_STATES; i++) {
532 if (s->states[i].in_use)
533 curl_clean_state(&s->states[i]);
534 if (s->states[i].curl) {
535 curl_easy_cleanup(s->states[i].curl);
536 s->states[i].curl = NULL;
538 if (s->states[i].orig_buf) {
539 g_free(s->states[i].orig_buf);
540 s->states[i].orig_buf = NULL;
543 if (s->multi)
544 curl_multi_cleanup(s->multi);
545 if (s->url)
546 free(s->url);
549 static int64_t curl_getlength(BlockDriverState *bs)
551 BDRVCURLState *s = bs->opaque;
552 return s->len;
555 static BlockDriver bdrv_http = {
556 .format_name = "http",
557 .protocol_name = "http",
559 .instance_size = sizeof(BDRVCURLState),
560 .bdrv_file_open = curl_open,
561 .bdrv_close = curl_close,
562 .bdrv_getlength = curl_getlength,
564 .bdrv_aio_readv = curl_aio_readv,
567 static BlockDriver bdrv_https = {
568 .format_name = "https",
569 .protocol_name = "https",
571 .instance_size = sizeof(BDRVCURLState),
572 .bdrv_file_open = curl_open,
573 .bdrv_close = curl_close,
574 .bdrv_getlength = curl_getlength,
576 .bdrv_aio_readv = curl_aio_readv,
579 static BlockDriver bdrv_ftp = {
580 .format_name = "ftp",
581 .protocol_name = "ftp",
583 .instance_size = sizeof(BDRVCURLState),
584 .bdrv_file_open = curl_open,
585 .bdrv_close = curl_close,
586 .bdrv_getlength = curl_getlength,
588 .bdrv_aio_readv = curl_aio_readv,
591 static BlockDriver bdrv_ftps = {
592 .format_name = "ftps",
593 .protocol_name = "ftps",
595 .instance_size = sizeof(BDRVCURLState),
596 .bdrv_file_open = curl_open,
597 .bdrv_close = curl_close,
598 .bdrv_getlength = curl_getlength,
600 .bdrv_aio_readv = curl_aio_readv,
603 static BlockDriver bdrv_tftp = {
604 .format_name = "tftp",
605 .protocol_name = "tftp",
607 .instance_size = sizeof(BDRVCURLState),
608 .bdrv_file_open = curl_open,
609 .bdrv_close = curl_close,
610 .bdrv_getlength = curl_getlength,
612 .bdrv_aio_readv = curl_aio_readv,
615 static void curl_block_init(void)
617 bdrv_register(&bdrv_http);
618 bdrv_register(&bdrv_https);
619 bdrv_register(&bdrv_ftp);
620 bdrv_register(&bdrv_ftps);
621 bdrv_register(&bdrv_tftp);
624 block_init(curl_block_init);