]> git.proxmox.com Git - mirror_qemu.git/blob - block/curl.c
curl: Fix hang reading from slow connections
[mirror_qemu.git] / block / curl.c
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"
25 #include "block/block_int.h"
26 #include <curl/curl.h>
27
28 // #define DEBUG
29 // #define DEBUG_VERBOSE
30
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
36
37 #if LIBCURL_VERSION_NUM >= 0x071000
38 /* The multi interface timer callback was introduced in 7.16.0 */
39 #define NEED_CURL_TIMER_CALLBACK
40 #endif
41
42 #define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \
43 CURLPROTO_FTP | CURLPROTO_FTPS | \
44 CURLPROTO_TFTP)
45
46 #define CURL_NUM_STATES 8
47 #define CURL_NUM_ACB 8
48 #define SECTOR_SIZE 512
49 #define READ_AHEAD_SIZE (256 * 1024)
50
51 #define FIND_RET_NONE 0
52 #define FIND_RET_OK 1
53 #define FIND_RET_WAIT 2
54
55 struct BDRVCURLState;
56
57 typedef struct CURLAIOCB {
58 BlockDriverAIOCB common;
59 QEMUBH *bh;
60 QEMUIOVector *qiov;
61
62 int64_t sector_num;
63 int nb_sectors;
64
65 size_t start;
66 size_t end;
67 } CURLAIOCB;
68
69 typedef struct CURLState
70 {
71 struct BDRVCURLState *s;
72 CURLAIOCB *acb[CURL_NUM_ACB];
73 CURL *curl;
74 curl_socket_t sock_fd;
75 char *orig_buf;
76 size_t buf_start;
77 size_t buf_off;
78 size_t buf_len;
79 char range[128];
80 char errmsg[CURL_ERROR_SIZE];
81 char in_use;
82 } CURLState;
83
84 typedef struct BDRVCURLState {
85 CURLM *multi;
86 QEMUTimer timer;
87 size_t len;
88 CURLState states[CURL_NUM_STATES];
89 char *url;
90 size_t readahead_size;
91 bool accept_range;
92 } BDRVCURLState;
93
94 static void curl_clean_state(CURLState *s);
95 static void curl_multi_do(void *arg);
96 static void curl_multi_read(void *arg);
97
98 #ifdef NEED_CURL_TIMER_CALLBACK
99 static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque)
100 {
101 BDRVCURLState *s = opaque;
102
103 DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms);
104 if (timeout_ms == -1) {
105 timer_del(&s->timer);
106 } else {
107 int64_t timeout_ns = (int64_t)timeout_ms * 1000 * 1000;
108 timer_mod(&s->timer,
109 qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + timeout_ns);
110 }
111 return 0;
112 }
113 #endif
114
115 static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
116 void *s, void *sp)
117 {
118 CURLState *state = NULL;
119 curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&state);
120 state->sock_fd = fd;
121
122 DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd);
123 switch (action) {
124 case CURL_POLL_IN:
125 qemu_aio_set_fd_handler(fd, curl_multi_read, NULL, state);
126 break;
127 case CURL_POLL_OUT:
128 qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, state);
129 break;
130 case CURL_POLL_INOUT:
131 qemu_aio_set_fd_handler(fd, curl_multi_read, curl_multi_do, state);
132 break;
133 case CURL_POLL_REMOVE:
134 qemu_aio_set_fd_handler(fd, NULL, NULL, NULL);
135 break;
136 }
137
138 return 0;
139 }
140
141 static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
142 {
143 BDRVCURLState *s = opaque;
144 size_t realsize = size * nmemb;
145 const char *accept_line = "Accept-Ranges: bytes";
146
147 if (realsize >= strlen(accept_line)
148 && strncmp((char *)ptr, accept_line, strlen(accept_line)) == 0) {
149 s->accept_range = true;
150 }
151
152 return realsize;
153 }
154
155 static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
156 {
157 CURLState *s = ((CURLState*)opaque);
158 size_t realsize = size * nmemb;
159 int i;
160
161 DPRINTF("CURL: Just reading %zd bytes\n", realsize);
162
163 if (!s || !s->orig_buf)
164 return 0;
165
166 if (s->buf_off >= s->buf_len) {
167 /* buffer full, read nothing */
168 return 0;
169 }
170 realsize = MIN(realsize, s->buf_len - s->buf_off);
171 memcpy(s->orig_buf + s->buf_off, ptr, realsize);
172 s->buf_off += realsize;
173
174 for(i=0; i<CURL_NUM_ACB; i++) {
175 CURLAIOCB *acb = s->acb[i];
176
177 if (!acb)
178 continue;
179
180 if ((s->buf_off >= acb->end)) {
181 qemu_iovec_from_buf(acb->qiov, 0, s->orig_buf + acb->start,
182 acb->end - acb->start);
183 acb->common.cb(acb->common.opaque, 0);
184 qemu_aio_release(acb);
185 s->acb[i] = NULL;
186 }
187 }
188
189 return realsize;
190 }
191
192 static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
193 CURLAIOCB *acb)
194 {
195 int i;
196 size_t end = start + len;
197
198 for (i=0; i<CURL_NUM_STATES; i++) {
199 CURLState *state = &s->states[i];
200 size_t buf_end = (state->buf_start + state->buf_off);
201 size_t buf_fend = (state->buf_start + state->buf_len);
202
203 if (!state->orig_buf)
204 continue;
205 if (!state->buf_off)
206 continue;
207
208 // Does the existing buffer cover our section?
209 if ((start >= state->buf_start) &&
210 (start <= buf_end) &&
211 (end >= state->buf_start) &&
212 (end <= buf_end))
213 {
214 char *buf = state->orig_buf + (start - state->buf_start);
215
216 qemu_iovec_from_buf(acb->qiov, 0, buf, len);
217 acb->common.cb(acb->common.opaque, 0);
218
219 return FIND_RET_OK;
220 }
221
222 // Wait for unfinished chunks
223 if (state->in_use &&
224 (start >= state->buf_start) &&
225 (start <= buf_fend) &&
226 (end >= state->buf_start) &&
227 (end <= buf_fend))
228 {
229 int j;
230
231 acb->start = start - state->buf_start;
232 acb->end = acb->start + len;
233
234 for (j=0; j<CURL_NUM_ACB; j++) {
235 if (!state->acb[j]) {
236 state->acb[j] = acb;
237 return FIND_RET_WAIT;
238 }
239 }
240 }
241 }
242
243 return FIND_RET_NONE;
244 }
245
246 static void curl_multi_check_completion(BDRVCURLState *s)
247 {
248 int msgs_in_queue;
249
250 /* Try to find done transfers, so we can free the easy
251 * handle again. */
252 for (;;) {
253 CURLMsg *msg;
254 msg = curl_multi_info_read(s->multi, &msgs_in_queue);
255
256 /* Quit when there are no more completions */
257 if (!msg)
258 break;
259
260 if (msg->msg == CURLMSG_DONE) {
261 CURLState *state = NULL;
262 curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE,
263 (char **)&state);
264
265 /* ACBs for successful messages get completed in curl_read_cb */
266 if (msg->data.result != CURLE_OK) {
267 int i;
268 for (i = 0; i < CURL_NUM_ACB; i++) {
269 CURLAIOCB *acb = state->acb[i];
270
271 if (acb == NULL) {
272 continue;
273 }
274
275 acb->common.cb(acb->common.opaque, -EIO);
276 qemu_aio_release(acb);
277 state->acb[i] = NULL;
278 }
279 }
280
281 curl_clean_state(state);
282 break;
283 }
284 }
285 }
286
287 static void curl_multi_do(void *arg)
288 {
289 CURLState *s = (CURLState *)arg;
290 int running;
291 int r;
292
293 if (!s->s->multi) {
294 return;
295 }
296
297 do {
298 r = curl_multi_socket_action(s->s->multi, s->sock_fd, 0, &running);
299 } while(r == CURLM_CALL_MULTI_PERFORM);
300
301 }
302
303 static void curl_multi_read(void *arg)
304 {
305 CURLState *s = (CURLState *)arg;
306
307 curl_multi_do(arg);
308 curl_multi_check_completion(s->s);
309 }
310
311 static void curl_multi_timeout_do(void *arg)
312 {
313 #ifdef NEED_CURL_TIMER_CALLBACK
314 BDRVCURLState *s = (BDRVCURLState *)arg;
315 int running;
316
317 if (!s->multi) {
318 return;
319 }
320
321 curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
322
323 curl_multi_check_completion(s);
324 #else
325 abort();
326 #endif
327 }
328
329 static CURLState *curl_init_state(BDRVCURLState *s)
330 {
331 CURLState *state = NULL;
332 int i, j;
333
334 do {
335 for (i=0; i<CURL_NUM_STATES; i++) {
336 for (j=0; j<CURL_NUM_ACB; j++)
337 if (s->states[i].acb[j])
338 continue;
339 if (s->states[i].in_use)
340 continue;
341
342 state = &s->states[i];
343 state->in_use = 1;
344 break;
345 }
346 if (!state) {
347 qemu_aio_wait();
348 }
349 } while(!state);
350
351 if (!state->curl) {
352 state->curl = curl_easy_init();
353 if (!state->curl) {
354 return NULL;
355 }
356 curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
357 curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5);
358 curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION,
359 (void *)curl_read_cb);
360 curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state);
361 curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state);
362 curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1);
363 curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1);
364 curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1);
365 curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
366 curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1);
367
368 /* Restrict supported protocols to avoid security issues in the more
369 * obscure protocols. For example, do not allow POP3/SMTP/IMAP see
370 * CVE-2013-0249.
371 *
372 * Restricting protocols is only supported from 7.19.4 upwards.
373 */
374 #if LIBCURL_VERSION_NUM >= 0x071304
375 curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS);
376 curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS);
377 #endif
378
379 #ifdef DEBUG_VERBOSE
380 curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1);
381 #endif
382 }
383
384 state->s = s;
385
386 return state;
387 }
388
389 static void curl_clean_state(CURLState *s)
390 {
391 if (s->s->multi)
392 curl_multi_remove_handle(s->s->multi, s->curl);
393 s->in_use = 0;
394 }
395
396 static void curl_parse_filename(const char *filename, QDict *options,
397 Error **errp)
398 {
399
400 #define RA_OPTSTR ":readahead="
401 char *file;
402 char *ra;
403 const char *ra_val;
404 int parse_state = 0;
405
406 file = g_strdup(filename);
407
408 /* Parse a trailing ":readahead=#:" param, if present. */
409 ra = file + strlen(file) - 1;
410 while (ra >= file) {
411 if (parse_state == 0) {
412 if (*ra == ':') {
413 parse_state++;
414 } else {
415 break;
416 }
417 } else if (parse_state == 1) {
418 if (*ra > '9' || *ra < '0') {
419 char *opt_start = ra - strlen(RA_OPTSTR) + 1;
420 if (opt_start > file &&
421 strncmp(opt_start, RA_OPTSTR, strlen(RA_OPTSTR)) == 0) {
422 ra_val = ra + 1;
423 ra -= strlen(RA_OPTSTR) - 1;
424 *ra = '\0';
425 qdict_put(options, "readahead", qstring_from_str(ra_val));
426 }
427 break;
428 }
429 }
430 ra--;
431 }
432
433 qdict_put(options, "url", qstring_from_str(file));
434
435 g_free(file);
436 }
437
438 static QemuOptsList runtime_opts = {
439 .name = "curl",
440 .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
441 .desc = {
442 {
443 .name = "url",
444 .type = QEMU_OPT_STRING,
445 .help = "URL to open",
446 },
447 {
448 .name = "readahead",
449 .type = QEMU_OPT_SIZE,
450 .help = "Readahead size",
451 },
452 { /* end of list */ }
453 },
454 };
455
456 static int curl_open(BlockDriverState *bs, QDict *options, int flags,
457 Error **errp)
458 {
459 BDRVCURLState *s = bs->opaque;
460 CURLState *state = NULL;
461 QemuOpts *opts;
462 Error *local_err = NULL;
463 const char *file;
464 double d;
465
466 static int inited = 0;
467
468 if (flags & BDRV_O_RDWR) {
469 error_setg(errp, "curl block device does not support writes");
470 return -EROFS;
471 }
472
473 opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
474 qemu_opts_absorb_qdict(opts, options, &local_err);
475 if (local_err) {
476 error_propagate(errp, local_err);
477 goto out_noclean;
478 }
479
480 s->readahead_size = qemu_opt_get_size(opts, "readahead", READ_AHEAD_SIZE);
481 if ((s->readahead_size & 0x1ff) != 0) {
482 error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512",
483 s->readahead_size);
484 goto out_noclean;
485 }
486
487 file = qemu_opt_get(opts, "url");
488 if (file == NULL) {
489 error_setg(errp, "curl block driver requires an 'url' option");
490 goto out_noclean;
491 }
492
493 if (!inited) {
494 curl_global_init(CURL_GLOBAL_ALL);
495 inited = 1;
496 }
497
498 DPRINTF("CURL: Opening %s\n", file);
499 s->url = g_strdup(file);
500 state = curl_init_state(s);
501 if (!state)
502 goto out_noclean;
503
504 // Get file size
505
506 s->accept_range = false;
507 curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1);
508 curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION,
509 curl_header_cb);
510 curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s);
511 if (curl_easy_perform(state->curl))
512 goto out;
513 curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
514 if (d)
515 s->len = (size_t)d;
516 else if(!s->len)
517 goto out;
518 if ((!strncasecmp(s->url, "http://", strlen("http://"))
519 || !strncasecmp(s->url, "https://", strlen("https://")))
520 && !s->accept_range) {
521 pstrcpy(state->errmsg, CURL_ERROR_SIZE,
522 "Server does not support 'range' (byte ranges).");
523 goto out;
524 }
525 DPRINTF("CURL: Size = %zd\n", s->len);
526
527 curl_clean_state(state);
528 curl_easy_cleanup(state->curl);
529 state->curl = NULL;
530
531 aio_timer_init(bdrv_get_aio_context(bs), &s->timer,
532 QEMU_CLOCK_REALTIME, SCALE_NS,
533 curl_multi_timeout_do, s);
534
535 // Now we know the file exists and its size, so let's
536 // initialize the multi interface!
537
538 s->multi = curl_multi_init();
539 curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb);
540 #ifdef NEED_CURL_TIMER_CALLBACK
541 curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s);
542 curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb);
543 #endif
544
545 qemu_opts_del(opts);
546 return 0;
547
548 out:
549 error_setg(errp, "CURL: Error opening file: %s", state->errmsg);
550 curl_easy_cleanup(state->curl);
551 state->curl = NULL;
552 out_noclean:
553 g_free(s->url);
554 qemu_opts_del(opts);
555 return -EINVAL;
556 }
557
558 static void curl_aio_cancel(BlockDriverAIOCB *blockacb)
559 {
560 // Do we have to implement canceling? Seems to work without...
561 }
562
563 static const AIOCBInfo curl_aiocb_info = {
564 .aiocb_size = sizeof(CURLAIOCB),
565 .cancel = curl_aio_cancel,
566 };
567
568
569 static void curl_readv_bh_cb(void *p)
570 {
571 CURLState *state;
572 int running;
573
574 CURLAIOCB *acb = p;
575 BDRVCURLState *s = acb->common.bs->opaque;
576
577 qemu_bh_delete(acb->bh);
578 acb->bh = NULL;
579
580 size_t start = acb->sector_num * SECTOR_SIZE;
581 size_t end;
582
583 // In case we have the requested data already (e.g. read-ahead),
584 // we can just call the callback and be done.
585 switch (curl_find_buf(s, start, acb->nb_sectors * SECTOR_SIZE, acb)) {
586 case FIND_RET_OK:
587 qemu_aio_release(acb);
588 // fall through
589 case FIND_RET_WAIT:
590 return;
591 default:
592 break;
593 }
594
595 // No cache found, so let's start a new request
596 state = curl_init_state(s);
597 if (!state) {
598 acb->common.cb(acb->common.opaque, -EIO);
599 qemu_aio_release(acb);
600 return;
601 }
602
603 acb->start = 0;
604 acb->end = (acb->nb_sectors * SECTOR_SIZE);
605
606 state->buf_off = 0;
607 if (state->orig_buf)
608 g_free(state->orig_buf);
609 state->buf_start = start;
610 state->buf_len = acb->end + s->readahead_size;
611 end = MIN(start + state->buf_len, s->len) - 1;
612 state->orig_buf = g_malloc(state->buf_len);
613 state->acb[0] = acb;
614
615 snprintf(state->range, 127, "%zd-%zd", start, end);
616 DPRINTF("CURL (AIO): Reading %d at %zd (%s)\n",
617 (acb->nb_sectors * SECTOR_SIZE), start, state->range);
618 curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range);
619
620 curl_multi_add_handle(s->multi, state->curl);
621
622 /* Tell curl it needs to kick things off */
623 curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
624 }
625
626 static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs,
627 int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
628 BlockDriverCompletionFunc *cb, void *opaque)
629 {
630 CURLAIOCB *acb;
631
632 acb = qemu_aio_get(&curl_aiocb_info, bs, cb, opaque);
633
634 acb->qiov = qiov;
635 acb->sector_num = sector_num;
636 acb->nb_sectors = nb_sectors;
637
638 acb->bh = qemu_bh_new(curl_readv_bh_cb, acb);
639 qemu_bh_schedule(acb->bh);
640 return &acb->common;
641 }
642
643 static void curl_close(BlockDriverState *bs)
644 {
645 BDRVCURLState *s = bs->opaque;
646 int i;
647
648 DPRINTF("CURL: Close\n");
649 for (i=0; i<CURL_NUM_STATES; i++) {
650 if (s->states[i].in_use)
651 curl_clean_state(&s->states[i]);
652 if (s->states[i].curl) {
653 curl_easy_cleanup(s->states[i].curl);
654 s->states[i].curl = NULL;
655 }
656 if (s->states[i].orig_buf) {
657 g_free(s->states[i].orig_buf);
658 s->states[i].orig_buf = NULL;
659 }
660 }
661 if (s->multi)
662 curl_multi_cleanup(s->multi);
663
664 timer_del(&s->timer);
665
666 g_free(s->url);
667 }
668
669 static int64_t curl_getlength(BlockDriverState *bs)
670 {
671 BDRVCURLState *s = bs->opaque;
672 return s->len;
673 }
674
675 static BlockDriver bdrv_http = {
676 .format_name = "http",
677 .protocol_name = "http",
678
679 .instance_size = sizeof(BDRVCURLState),
680 .bdrv_parse_filename = curl_parse_filename,
681 .bdrv_file_open = curl_open,
682 .bdrv_close = curl_close,
683 .bdrv_getlength = curl_getlength,
684
685 .bdrv_aio_readv = curl_aio_readv,
686 };
687
688 static BlockDriver bdrv_https = {
689 .format_name = "https",
690 .protocol_name = "https",
691
692 .instance_size = sizeof(BDRVCURLState),
693 .bdrv_parse_filename = curl_parse_filename,
694 .bdrv_file_open = curl_open,
695 .bdrv_close = curl_close,
696 .bdrv_getlength = curl_getlength,
697
698 .bdrv_aio_readv = curl_aio_readv,
699 };
700
701 static BlockDriver bdrv_ftp = {
702 .format_name = "ftp",
703 .protocol_name = "ftp",
704
705 .instance_size = sizeof(BDRVCURLState),
706 .bdrv_parse_filename = curl_parse_filename,
707 .bdrv_file_open = curl_open,
708 .bdrv_close = curl_close,
709 .bdrv_getlength = curl_getlength,
710
711 .bdrv_aio_readv = curl_aio_readv,
712 };
713
714 static BlockDriver bdrv_ftps = {
715 .format_name = "ftps",
716 .protocol_name = "ftps",
717
718 .instance_size = sizeof(BDRVCURLState),
719 .bdrv_parse_filename = curl_parse_filename,
720 .bdrv_file_open = curl_open,
721 .bdrv_close = curl_close,
722 .bdrv_getlength = curl_getlength,
723
724 .bdrv_aio_readv = curl_aio_readv,
725 };
726
727 static BlockDriver bdrv_tftp = {
728 .format_name = "tftp",
729 .protocol_name = "tftp",
730
731 .instance_size = sizeof(BDRVCURLState),
732 .bdrv_parse_filename = curl_parse_filename,
733 .bdrv_file_open = curl_open,
734 .bdrv_close = curl_close,
735 .bdrv_getlength = curl_getlength,
736
737 .bdrv_aio_readv = curl_aio_readv,
738 };
739
740 static void curl_block_init(void)
741 {
742 bdrv_register(&bdrv_http);
743 bdrv_register(&bdrv_https);
744 bdrv_register(&bdrv_ftp);
745 bdrv_register(&bdrv_ftps);
746 bdrv_register(&bdrv_tftp);
747 }
748
749 block_init(curl_block_init);