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