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); 750