]> git.proxmox.com Git - qemu.git/blame - block/curl.c
block: move include files to include/block/
[qemu.git] / block / curl.c
CommitLineData
769ce76d
AG
1/*
2 * QEMU Block driver for CURL images
3 *
4 * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
5 *
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:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
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.
23 */
24#include "qemu-common.h"
737e150e 25#include "block/block_int.h"
769ce76d
AG
26#include <curl/curl.h>
27
28// #define DEBUG
29// #define DEBUG_VERBOSE
30
31#ifdef DEBUG_CURL
d0f2c4c6 32#define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0)
769ce76d 33#else
d0f2c4c6 34#define DPRINTF(fmt, ...) do { } while (0)
769ce76d
AG
35#endif
36
37#define CURL_NUM_STATES 8
38#define CURL_NUM_ACB 8
39#define SECTOR_SIZE 512
40#define READ_AHEAD_SIZE (256 * 1024)
41
42#define FIND_RET_NONE 0
43#define FIND_RET_OK 1
44#define FIND_RET_WAIT 2
45
46struct BDRVCURLState;
47
48typedef struct CURLAIOCB {
49 BlockDriverAIOCB common;
363c3c85 50 QEMUBH *bh;
769ce76d 51 QEMUIOVector *qiov;
363c3c85
NT
52
53 int64_t sector_num;
54 int nb_sectors;
55
769ce76d
AG
56 size_t start;
57 size_t end;
58} CURLAIOCB;
59
60typedef struct CURLState
61{
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;
73
74typedef struct BDRVCURLState {
75 CURLM *multi;
76 size_t len;
77 CURLState states[CURL_NUM_STATES];
78 char *url;
c76f4952 79 size_t readahead_size;
769ce76d
AG
80} BDRVCURLState;
81
82static void curl_clean_state(CURLState *s);
83static void curl_multi_do(void *arg);
c84dcdc1 84static int curl_aio_flush(void *opaque);
769ce76d
AG
85
86static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
87 void *s, void *sp)
88{
d0f2c4c6 89 DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd);
769ce76d
AG
90 switch (action) {
91 case CURL_POLL_IN:
bafbd6a1 92 qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, curl_aio_flush, s);
769ce76d
AG
93 break;
94 case CURL_POLL_OUT:
bafbd6a1 95 qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, curl_aio_flush, s);
769ce76d
AG
96 break;
97 case CURL_POLL_INOUT:
c84dcdc1 98 qemu_aio_set_fd_handler(fd, curl_multi_do, curl_multi_do,
bafbd6a1 99 curl_aio_flush, s);
769ce76d
AG
100 break;
101 case CURL_POLL_REMOVE:
bafbd6a1 102 qemu_aio_set_fd_handler(fd, NULL, NULL, NULL, NULL);
769ce76d
AG
103 break;
104 }
105
106 return 0;
107}
108
109static size_t curl_size_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
110{
111 CURLState *s = ((CURLState*)opaque);
112 size_t realsize = size * nmemb;
0bfcd599 113 size_t fsize;
769ce76d 114
0bfcd599 115 if(sscanf(ptr, "Content-Length: %zd", &fsize) == 1) {
769ce76d 116 s->s->len = fsize;
0bfcd599 117 }
769ce76d
AG
118
119 return realsize;
120}
121
122static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
123{
124 CURLState *s = ((CURLState*)opaque);
125 size_t realsize = size * nmemb;
126 int i;
127
0bfcd599 128 DPRINTF("CURL: Just reading %zd bytes\n", realsize);
769ce76d
AG
129
130 if (!s || !s->orig_buf)
131 goto read_end;
132
133 memcpy(s->orig_buf + s->buf_off, ptr, realsize);
134 s->buf_off += realsize;
135
136 for(i=0; i<CURL_NUM_ACB; i++) {
137 CURLAIOCB *acb = s->acb[i];
138
139 if (!acb)
140 continue;
141
142 if ((s->buf_off >= acb->end)) {
03396148
MT
143 qemu_iovec_from_buf(acb->qiov, 0, s->orig_buf + acb->start,
144 acb->end - acb->start);
769ce76d
AG
145 acb->common.cb(acb->common.opaque, 0);
146 qemu_aio_release(acb);
147 s->acb[i] = NULL;
148 }
149 }
150
151read_end:
152 return realsize;
153}
154
155static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
156 CURLAIOCB *acb)
157{
158 int i;
159 size_t end = start + len;
160
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);
165
166 if (!state->orig_buf)
167 continue;
168 if (!state->buf_off)
169 continue;
170
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))
176 {
177 char *buf = state->orig_buf + (start - state->buf_start);
178
03396148 179 qemu_iovec_from_buf(acb->qiov, 0, buf, len);
769ce76d
AG
180 acb->common.cb(acb->common.opaque, 0);
181
182 return FIND_RET_OK;
183 }
184
185 // Wait for unfinished chunks
186 if ((start >= state->buf_start) &&
187 (start <= buf_fend) &&
188 (end >= state->buf_start) &&
189 (end <= buf_fend))
190 {
191 int j;
192
193 acb->start = start - state->buf_start;
194 acb->end = acb->start + len;
195
196 for (j=0; j<CURL_NUM_ACB; j++) {
197 if (!state->acb[j]) {
198 state->acb[j] = acb;
199 return FIND_RET_WAIT;
200 }
201 }
202 }
203 }
204
205 return FIND_RET_NONE;
206}
207
208static void curl_multi_do(void *arg)
209{
210 BDRVCURLState *s = (BDRVCURLState *)arg;
211 int running;
212 int r;
213 int msgs_in_queue;
214
215 if (!s->multi)
216 return;
217
218 do {
219 r = curl_multi_socket_all(s->multi, &running);
220 } while(r == CURLM_CALL_MULTI_PERFORM);
221
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);
227
228 if (!msg)
229 break;
230 if (msg->msg == CURLMSG_NONE)
231 break;
232
233 switch (msg->msg) {
234 case CURLMSG_DONE:
235 {
236 CURLState *state = NULL;
237 curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char**)&state);
f785a5ae
NT
238
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];
244
245 if (acb == NULL) {
246 continue;
247 }
248
249 acb->common.cb(acb->common.opaque, -EIO);
250 qemu_aio_release(acb);
251 state->acb[i] = NULL;
252 }
253 }
254
769ce76d
AG
255 curl_clean_state(state);
256 break;
257 }
258 default:
259 msgs_in_queue = 0;
260 break;
261 }
262 } while(msgs_in_queue);
263}
264
265static CURLState *curl_init_state(BDRVCURLState *s)
266{
267 CURLState *state = NULL;
268 int i, j;
269
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;
277
278 state = &s->states[i];
279 state->in_use = 1;
280 break;
281 }
282 if (!state) {
fb7c8e8a 283 g_usleep(100);
769ce76d
AG
284 curl_multi_do(s);
285 }
286 } while(!state);
287
288 if (state->curl)
289 goto has_curl;
290
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);
df3cee1a 296 curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
769ce76d
AG
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);
f785a5ae
NT
303 curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1);
304
769ce76d
AG
305#ifdef DEBUG_VERBOSE
306 curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1);
307#endif
308
309has_curl:
310
311 state->s = s;
312
313 return state;
314}
315
316static void curl_clean_state(CURLState *s)
317{
318 if (s->s->multi)
319 curl_multi_remove_handle(s->s->multi, s->curl);
320 s->in_use = 0;
321}
322
323static int curl_open(BlockDriverState *bs, const char *filename, int flags)
324{
325 BDRVCURLState *s = bs->opaque;
326 CURLState *state = NULL;
327 double d;
c76f4952
N
328
329 #define RA_OPTSTR ":readahead="
330 char *file;
331 char *ra;
332 const char *ra_val;
333 int parse_state = 0;
334
769ce76d
AG
335 static int inited = 0;
336
7267c094 337 file = g_strdup(filename);
c76f4952
N
338 s->readahead_size = READ_AHEAD_SIZE;
339
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;
360 }
361 }
362 }
363 ra--;
364 }
365
366 if ((s->readahead_size & 0x1ff) != 0) {
48a402e6 367 fprintf(stderr, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512\n",
c76f4952
N
368 s->readahead_size);
369 goto out_noclean;
370 }
371
769ce76d
AG
372 if (!inited) {
373 curl_global_init(CURL_GLOBAL_ALL);
374 inited = 1;
375 }
376
d0f2c4c6 377 DPRINTF("CURL: Opening %s\n", file);
c76f4952 378 s->url = file;
769ce76d
AG
379 state = curl_init_state(s);
380 if (!state)
381 goto out_noclean;
382
383 // Get file size
384
385 curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1);
df3cee1a 386 curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_size_cb);
769ce76d
AG
387 if (curl_easy_perform(state->curl))
388 goto out;
389 curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
df3cee1a 390 curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
769ce76d
AG
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;
0bfcd599 396 DPRINTF("CURL: Size = %zd\n", s->len);
769ce76d
AG
397
398 curl_clean_state(state);
399 curl_easy_cleanup(state->curl);
400 state->curl = NULL;
401
402 // Now we know the file exists and its size, so let's
403 // initialize the multi interface!
404
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);
409
410 return 0;
411
412out:
413 fprintf(stderr, "CURL: Error opening file: %s\n", state->errmsg);
414 curl_easy_cleanup(state->curl);
415 state->curl = NULL;
416out_noclean:
7267c094 417 g_free(file);
769ce76d
AG
418 return -EINVAL;
419}
420
c84dcdc1
NT
421static int curl_aio_flush(void *opaque)
422{
423 BDRVCURLState *s = opaque;
424 int i, j;
425
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;
430 }
431 }
432 }
433 return 0;
434}
435
c16b5a2c
CH
436static void curl_aio_cancel(BlockDriverAIOCB *blockacb)
437{
438 // Do we have to implement canceling? Seems to work without...
439}
440
d7331bed 441static const AIOCBInfo curl_aiocb_info = {
c16b5a2c
CH
442 .aiocb_size = sizeof(CURLAIOCB),
443 .cancel = curl_aio_cancel,
444};
445
363c3c85
NT
446
447static void curl_readv_bh_cb(void *p)
769ce76d 448{
769ce76d
AG
449 CURLState *state;
450
363c3c85
NT
451 CURLAIOCB *acb = p;
452 BDRVCURLState *s = acb->common.bs->opaque;
769ce76d 453
363c3c85
NT
454 qemu_bh_delete(acb->bh);
455 acb->bh = NULL;
456
457 size_t start = acb->sector_num * SECTOR_SIZE;
458 size_t end;
769ce76d
AG
459
460 // In case we have the requested data already (e.g. read-ahead),
461 // we can just call the callback and be done.
363c3c85 462 switch (curl_find_buf(s, start, acb->nb_sectors * SECTOR_SIZE, acb)) {
769ce76d
AG
463 case FIND_RET_OK:
464 qemu_aio_release(acb);
465 // fall through
466 case FIND_RET_WAIT:
363c3c85 467 return;
769ce76d
AG
468 default:
469 break;
470 }
471
472 // No cache found, so let's start a new request
769ce76d 473 state = curl_init_state(s);
363c3c85
NT
474 if (!state) {
475 acb->common.cb(acb->common.opaque, -EIO);
476 qemu_aio_release(acb);
477 return;
478 }
769ce76d
AG
479
480 acb->start = 0;
363c3c85 481 acb->end = (acb->nb_sectors * SECTOR_SIZE);
769ce76d
AG
482
483 state->buf_off = 0;
484 if (state->orig_buf)
7267c094 485 g_free(state->orig_buf);
769ce76d 486 state->buf_start = start;
c76f4952 487 state->buf_len = acb->end + s->readahead_size;
769ce76d 488 end = MIN(start + state->buf_len, s->len) - 1;
7267c094 489 state->orig_buf = g_malloc(state->buf_len);
769ce76d
AG
490 state->acb[0] = acb;
491
0bfcd599
BS
492 snprintf(state->range, 127, "%zd-%zd", start, end);
493 DPRINTF("CURL (AIO): Reading %d at %zd (%s)\n",
363c3c85 494 (acb->nb_sectors * SECTOR_SIZE), start, state->range);
769ce76d
AG
495 curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range);
496
497 curl_multi_add_handle(s->multi, state->curl);
498 curl_multi_do(s);
499
363c3c85
NT
500}
501
502static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs,
503 int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
504 BlockDriverCompletionFunc *cb, void *opaque)
505{
506 CURLAIOCB *acb;
507
d7331bed 508 acb = qemu_aio_get(&curl_aiocb_info, bs, cb, opaque);
363c3c85 509
363c3c85
NT
510 acb->qiov = qiov;
511 acb->sector_num = sector_num;
512 acb->nb_sectors = nb_sectors;
513
514 acb->bh = qemu_bh_new(curl_readv_bh_cb, acb);
515
516 if (!acb->bh) {
517 DPRINTF("CURL: qemu_bh_new failed\n");
518 return NULL;
519 }
520
521 qemu_bh_schedule(acb->bh);
769ce76d
AG
522 return &acb->common;
523}
524
769ce76d
AG
525static void curl_close(BlockDriverState *bs)
526{
527 BDRVCURLState *s = bs->opaque;
528 int i;
529
d0f2c4c6 530 DPRINTF("CURL: Close\n");
769ce76d
AG
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;
537 }
538 if (s->states[i].orig_buf) {
7267c094 539 g_free(s->states[i].orig_buf);
769ce76d
AG
540 s->states[i].orig_buf = NULL;
541 }
542 }
543 if (s->multi)
544 curl_multi_cleanup(s->multi);
45724d6d 545 g_free(s->url);
769ce76d
AG
546}
547
548static int64_t curl_getlength(BlockDriverState *bs)
549{
550 BDRVCURLState *s = bs->opaque;
551 return s->len;
552}
553
554static BlockDriver bdrv_http = {
555 .format_name = "http",
556 .protocol_name = "http",
557
558 .instance_size = sizeof(BDRVCURLState),
66f82cee 559 .bdrv_file_open = curl_open,
769ce76d
AG
560 .bdrv_close = curl_close,
561 .bdrv_getlength = curl_getlength,
562
769ce76d 563 .bdrv_aio_readv = curl_aio_readv,
769ce76d
AG
564};
565
566static BlockDriver bdrv_https = {
567 .format_name = "https",
568 .protocol_name = "https",
569
570 .instance_size = sizeof(BDRVCURLState),
66f82cee 571 .bdrv_file_open = curl_open,
769ce76d
AG
572 .bdrv_close = curl_close,
573 .bdrv_getlength = curl_getlength,
574
769ce76d 575 .bdrv_aio_readv = curl_aio_readv,
769ce76d
AG
576};
577
578static BlockDriver bdrv_ftp = {
579 .format_name = "ftp",
580 .protocol_name = "ftp",
581
582 .instance_size = sizeof(BDRVCURLState),
66f82cee 583 .bdrv_file_open = curl_open,
769ce76d
AG
584 .bdrv_close = curl_close,
585 .bdrv_getlength = curl_getlength,
586
769ce76d 587 .bdrv_aio_readv = curl_aio_readv,
769ce76d
AG
588};
589
590static BlockDriver bdrv_ftps = {
591 .format_name = "ftps",
592 .protocol_name = "ftps",
593
594 .instance_size = sizeof(BDRVCURLState),
66f82cee 595 .bdrv_file_open = curl_open,
769ce76d
AG
596 .bdrv_close = curl_close,
597 .bdrv_getlength = curl_getlength,
598
769ce76d 599 .bdrv_aio_readv = curl_aio_readv,
769ce76d
AG
600};
601
602static BlockDriver bdrv_tftp = {
603 .format_name = "tftp",
604 .protocol_name = "tftp",
605
606 .instance_size = sizeof(BDRVCURLState),
66f82cee 607 .bdrv_file_open = curl_open,
769ce76d
AG
608 .bdrv_close = curl_close,
609 .bdrv_getlength = curl_getlength,
610
769ce76d 611 .bdrv_aio_readv = curl_aio_readv,
769ce76d
AG
612};
613
614static void curl_block_init(void)
615{
616 bdrv_register(&bdrv_http);
617 bdrv_register(&bdrv_https);
618 bdrv_register(&bdrv_ftp);
619 bdrv_register(&bdrv_ftps);
620 bdrv_register(&bdrv_tftp);
621}
622
623block_init(curl_block_init);