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