2 * QEMU Block driver for CURL images
4 * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
24 #include "qemu/osdep.h"
25 #include "qapi/error.h"
26 #include "qemu-common.h"
27 #include "qemu/error-report.h"
28 #include "block/block_int.h"
29 #include "qapi/qmp/qbool.h"
30 #include "qapi/qmp/qstring.h"
31 #include "crypto/secret.h"
32 #include <curl/curl.h>
35 // #define DEBUG_VERBOSE
38 #define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0)
40 #define DPRINTF(fmt, ...) do { } while (0)
43 #if LIBCURL_VERSION_NUM >= 0x071000
44 /* The multi interface timer callback was introduced in 7.16.0 */
45 #define NEED_CURL_TIMER_CALLBACK
46 #define HAVE_SOCKET_ACTION
49 #ifndef HAVE_SOCKET_ACTION
50 /* If curl_multi_socket_action isn't available, define it statically here in
51 * terms of curl_multi_socket. Note that ev_bitmask will be ignored, which is
52 * less efficient but still safe. */
53 static CURLMcode
__curl_multi_socket_action(CURLM
*multi_handle
,
58 return curl_multi_socket(multi_handle
, sockfd
, running_handles
);
60 #define curl_multi_socket_action __curl_multi_socket_action
63 #define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \
64 CURLPROTO_FTP | CURLPROTO_FTPS | \
67 #define CURL_NUM_STATES 8
68 #define CURL_NUM_ACB 8
69 #define SECTOR_SIZE 512
70 #define READ_AHEAD_DEFAULT (256 * 1024)
71 #define CURL_TIMEOUT_DEFAULT 5
72 #define CURL_TIMEOUT_MAX 10000
74 #define FIND_RET_NONE 0
76 #define FIND_RET_WAIT 2
78 #define CURL_BLOCK_OPT_URL "url"
79 #define CURL_BLOCK_OPT_READAHEAD "readahead"
80 #define CURL_BLOCK_OPT_SSLVERIFY "sslverify"
81 #define CURL_BLOCK_OPT_TIMEOUT "timeout"
82 #define CURL_BLOCK_OPT_COOKIE "cookie"
83 #define CURL_BLOCK_OPT_USERNAME "username"
84 #define CURL_BLOCK_OPT_PASSWORD_SECRET "password-secret"
85 #define CURL_BLOCK_OPT_PROXY_USERNAME "proxy-username"
86 #define CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET "proxy-password-secret"
90 typedef struct CURLAIOCB
{
102 typedef struct CURLState
104 struct BDRVCURLState
*s
;
105 CURLAIOCB
*acb
[CURL_NUM_ACB
];
107 curl_socket_t sock_fd
;
113 char errmsg
[CURL_ERROR_SIZE
];
117 typedef struct BDRVCURLState
{
121 CURLState states
[CURL_NUM_STATES
];
123 size_t readahead_size
;
128 AioContext
*aio_context
;
135 static void curl_clean_state(CURLState
*s
);
136 static void curl_multi_do(void *arg
);
137 static void curl_multi_read(void *arg
);
139 #ifdef NEED_CURL_TIMER_CALLBACK
140 static int curl_timer_cb(CURLM
*multi
, long timeout_ms
, void *opaque
)
142 BDRVCURLState
*s
= opaque
;
144 DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms
);
145 if (timeout_ms
== -1) {
146 timer_del(&s
->timer
);
148 int64_t timeout_ns
= (int64_t)timeout_ms
* 1000 * 1000;
150 qemu_clock_get_ns(QEMU_CLOCK_REALTIME
) + timeout_ns
);
156 static int curl_sock_cb(CURL
*curl
, curl_socket_t fd
, int action
,
157 void *userp
, void *sp
)
160 CURLState
*state
= NULL
;
161 curl_easy_getinfo(curl
, CURLINFO_PRIVATE
, (char **)&state
);
165 DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action
, fd
);
168 aio_set_fd_handler(s
->aio_context
, fd
, false,
169 curl_multi_read
, NULL
, state
);
172 aio_set_fd_handler(s
->aio_context
, fd
, false,
173 NULL
, curl_multi_do
, state
);
175 case CURL_POLL_INOUT
:
176 aio_set_fd_handler(s
->aio_context
, fd
, false,
177 curl_multi_read
, curl_multi_do
, state
);
179 case CURL_POLL_REMOVE
:
180 aio_set_fd_handler(s
->aio_context
, fd
, false,
188 static size_t curl_header_cb(void *ptr
, size_t size
, size_t nmemb
, void *opaque
)
190 BDRVCURLState
*s
= opaque
;
191 size_t realsize
= size
* nmemb
;
192 const char *accept_line
= "Accept-Ranges: bytes";
194 if (realsize
>= strlen(accept_line
)
195 && strncmp((char *)ptr
, accept_line
, strlen(accept_line
)) == 0) {
196 s
->accept_range
= true;
202 static size_t curl_read_cb(void *ptr
, size_t size
, size_t nmemb
, void *opaque
)
204 CURLState
*s
= ((CURLState
*)opaque
);
205 size_t realsize
= size
* nmemb
;
208 DPRINTF("CURL: Just reading %zd bytes\n", realsize
);
210 if (!s
|| !s
->orig_buf
)
213 if (s
->buf_off
>= s
->buf_len
) {
214 /* buffer full, read nothing */
217 realsize
= MIN(realsize
, s
->buf_len
- s
->buf_off
);
218 memcpy(s
->orig_buf
+ s
->buf_off
, ptr
, realsize
);
219 s
->buf_off
+= realsize
;
221 for(i
=0; i
<CURL_NUM_ACB
; i
++) {
222 CURLAIOCB
*acb
= s
->acb
[i
];
227 if ((s
->buf_off
>= acb
->end
)) {
228 qemu_iovec_from_buf(acb
->qiov
, 0, s
->orig_buf
+ acb
->start
,
229 acb
->end
- acb
->start
);
230 acb
->common
.cb(acb
->common
.opaque
, 0);
239 static int curl_find_buf(BDRVCURLState
*s
, size_t start
, size_t len
,
243 size_t end
= start
+ len
;
245 for (i
=0; i
<CURL_NUM_STATES
; i
++) {
246 CURLState
*state
= &s
->states
[i
];
247 size_t buf_end
= (state
->buf_start
+ state
->buf_off
);
248 size_t buf_fend
= (state
->buf_start
+ state
->buf_len
);
250 if (!state
->orig_buf
)
255 // Does the existing buffer cover our section?
256 if ((start
>= state
->buf_start
) &&
257 (start
<= buf_end
) &&
258 (end
>= state
->buf_start
) &&
261 char *buf
= state
->orig_buf
+ (start
- state
->buf_start
);
263 qemu_iovec_from_buf(acb
->qiov
, 0, buf
, len
);
264 acb
->common
.cb(acb
->common
.opaque
, 0);
269 // Wait for unfinished chunks
271 (start
>= state
->buf_start
) &&
272 (start
<= buf_fend
) &&
273 (end
>= state
->buf_start
) &&
278 acb
->start
= start
- state
->buf_start
;
279 acb
->end
= acb
->start
+ len
;
281 for (j
=0; j
<CURL_NUM_ACB
; j
++) {
282 if (!state
->acb
[j
]) {
284 return FIND_RET_WAIT
;
290 return FIND_RET_NONE
;
293 static void curl_multi_check_completion(BDRVCURLState
*s
)
297 /* Try to find done transfers, so we can free the easy
301 msg
= curl_multi_info_read(s
->multi
, &msgs_in_queue
);
303 /* Quit when there are no more completions */
307 if (msg
->msg
== CURLMSG_DONE
) {
308 CURLState
*state
= NULL
;
309 curl_easy_getinfo(msg
->easy_handle
, CURLINFO_PRIVATE
,
312 /* ACBs for successful messages get completed in curl_read_cb */
313 if (msg
->data
.result
!= CURLE_OK
) {
315 static int errcount
= 100;
317 /* Don't lose the original error message from curl, since
318 * it contains extra data.
321 error_report("curl: %s", state
->errmsg
);
322 if (--errcount
== 0) {
323 error_report("curl: further errors suppressed");
327 for (i
= 0; i
< CURL_NUM_ACB
; i
++) {
328 CURLAIOCB
*acb
= state
->acb
[i
];
334 acb
->common
.cb(acb
->common
.opaque
, -EPROTO
);
336 state
->acb
[i
] = NULL
;
340 curl_clean_state(state
);
346 static void curl_multi_do(void *arg
)
348 CURLState
*s
= (CURLState
*)arg
;
357 r
= curl_multi_socket_action(s
->s
->multi
, s
->sock_fd
, 0, &running
);
358 } while(r
== CURLM_CALL_MULTI_PERFORM
);
362 static void curl_multi_read(void *arg
)
364 CURLState
*s
= (CURLState
*)arg
;
367 curl_multi_check_completion(s
->s
);
370 static void curl_multi_timeout_do(void *arg
)
372 #ifdef NEED_CURL_TIMER_CALLBACK
373 BDRVCURLState
*s
= (BDRVCURLState
*)arg
;
380 curl_multi_socket_action(s
->multi
, CURL_SOCKET_TIMEOUT
, 0, &running
);
382 curl_multi_check_completion(s
);
388 static CURLState
*curl_init_state(BlockDriverState
*bs
, BDRVCURLState
*s
)
390 CURLState
*state
= NULL
;
394 for (i
=0; i
<CURL_NUM_STATES
; i
++) {
395 for (j
=0; j
<CURL_NUM_ACB
; j
++)
396 if (s
->states
[i
].acb
[j
])
398 if (s
->states
[i
].in_use
)
401 state
= &s
->states
[i
];
406 aio_poll(bdrv_get_aio_context(bs
), true);
411 state
->curl
= curl_easy_init();
415 curl_easy_setopt(state
->curl
, CURLOPT_URL
, s
->url
);
416 curl_easy_setopt(state
->curl
, CURLOPT_SSL_VERIFYPEER
,
417 (long) s
->sslverify
);
419 curl_easy_setopt(state
->curl
, CURLOPT_COOKIE
, s
->cookie
);
421 curl_easy_setopt(state
->curl
, CURLOPT_TIMEOUT
, (long)s
->timeout
);
422 curl_easy_setopt(state
->curl
, CURLOPT_WRITEFUNCTION
,
423 (void *)curl_read_cb
);
424 curl_easy_setopt(state
->curl
, CURLOPT_WRITEDATA
, (void *)state
);
425 curl_easy_setopt(state
->curl
, CURLOPT_PRIVATE
, (void *)state
);
426 curl_easy_setopt(state
->curl
, CURLOPT_AUTOREFERER
, 1);
427 curl_easy_setopt(state
->curl
, CURLOPT_FOLLOWLOCATION
, 1);
428 curl_easy_setopt(state
->curl
, CURLOPT_NOSIGNAL
, 1);
429 curl_easy_setopt(state
->curl
, CURLOPT_ERRORBUFFER
, state
->errmsg
);
430 curl_easy_setopt(state
->curl
, CURLOPT_FAILONERROR
, 1);
433 curl_easy_setopt(state
->curl
, CURLOPT_USERNAME
, s
->username
);
436 curl_easy_setopt(state
->curl
, CURLOPT_PASSWORD
, s
->password
);
438 if (s
->proxyusername
) {
439 curl_easy_setopt(state
->curl
,
440 CURLOPT_PROXYUSERNAME
, s
->proxyusername
);
442 if (s
->proxypassword
) {
443 curl_easy_setopt(state
->curl
,
444 CURLOPT_PROXYPASSWORD
, s
->proxypassword
);
447 /* Restrict supported protocols to avoid security issues in the more
448 * obscure protocols. For example, do not allow POP3/SMTP/IMAP see
451 * Restricting protocols is only supported from 7.19.4 upwards.
453 #if LIBCURL_VERSION_NUM >= 0x071304
454 curl_easy_setopt(state
->curl
, CURLOPT_PROTOCOLS
, PROTOCOLS
);
455 curl_easy_setopt(state
->curl
, CURLOPT_REDIR_PROTOCOLS
, PROTOCOLS
);
459 curl_easy_setopt(state
->curl
, CURLOPT_VERBOSE
, 1);
468 static void curl_clean_state(CURLState
*s
)
471 curl_multi_remove_handle(s
->s
->multi
, s
->curl
);
475 static void curl_parse_filename(const char *filename
, QDict
*options
,
478 qdict_put(options
, CURL_BLOCK_OPT_URL
, qstring_from_str(filename
));
481 static void curl_detach_aio_context(BlockDriverState
*bs
)
483 BDRVCURLState
*s
= bs
->opaque
;
486 for (i
= 0; i
< CURL_NUM_STATES
; i
++) {
487 if (s
->states
[i
].in_use
) {
488 curl_clean_state(&s
->states
[i
]);
490 if (s
->states
[i
].curl
) {
491 curl_easy_cleanup(s
->states
[i
].curl
);
492 s
->states
[i
].curl
= NULL
;
494 g_free(s
->states
[i
].orig_buf
);
495 s
->states
[i
].orig_buf
= NULL
;
498 curl_multi_cleanup(s
->multi
);
502 timer_del(&s
->timer
);
505 static void curl_attach_aio_context(BlockDriverState
*bs
,
506 AioContext
*new_context
)
508 BDRVCURLState
*s
= bs
->opaque
;
510 aio_timer_init(new_context
, &s
->timer
,
511 QEMU_CLOCK_REALTIME
, SCALE_NS
,
512 curl_multi_timeout_do
, s
);
515 s
->multi
= curl_multi_init();
516 s
->aio_context
= new_context
;
517 curl_multi_setopt(s
->multi
, CURLMOPT_SOCKETFUNCTION
, curl_sock_cb
);
518 #ifdef NEED_CURL_TIMER_CALLBACK
519 curl_multi_setopt(s
->multi
, CURLMOPT_TIMERDATA
, s
);
520 curl_multi_setopt(s
->multi
, CURLMOPT_TIMERFUNCTION
, curl_timer_cb
);
524 static QemuOptsList runtime_opts
= {
526 .head
= QTAILQ_HEAD_INITIALIZER(runtime_opts
.head
),
529 .name
= CURL_BLOCK_OPT_URL
,
530 .type
= QEMU_OPT_STRING
,
531 .help
= "URL to open",
534 .name
= CURL_BLOCK_OPT_READAHEAD
,
535 .type
= QEMU_OPT_SIZE
,
536 .help
= "Readahead size",
539 .name
= CURL_BLOCK_OPT_SSLVERIFY
,
540 .type
= QEMU_OPT_BOOL
,
541 .help
= "Verify SSL certificate"
544 .name
= CURL_BLOCK_OPT_TIMEOUT
,
545 .type
= QEMU_OPT_NUMBER
,
546 .help
= "Curl timeout"
549 .name
= CURL_BLOCK_OPT_COOKIE
,
550 .type
= QEMU_OPT_STRING
,
551 .help
= "Pass the cookie or list of cookies with each request"
554 .name
= CURL_BLOCK_OPT_USERNAME
,
555 .type
= QEMU_OPT_STRING
,
556 .help
= "Username for HTTP auth"
559 .name
= CURL_BLOCK_OPT_PASSWORD_SECRET
,
560 .type
= QEMU_OPT_STRING
,
561 .help
= "ID of secret used as password for HTTP auth",
564 .name
= CURL_BLOCK_OPT_PROXY_USERNAME
,
565 .type
= QEMU_OPT_STRING
,
566 .help
= "Username for HTTP proxy auth"
569 .name
= CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET
,
570 .type
= QEMU_OPT_STRING
,
571 .help
= "ID of secret used as password for HTTP proxy auth",
573 { /* end of list */ }
578 static int curl_open(BlockDriverState
*bs
, QDict
*options
, int flags
,
581 BDRVCURLState
*s
= bs
->opaque
;
582 CURLState
*state
= NULL
;
584 Error
*local_err
= NULL
;
588 const char *secretid
;
590 static int inited
= 0;
592 if (flags
& BDRV_O_RDWR
) {
593 error_setg(errp
, "curl block device does not support writes");
597 opts
= qemu_opts_create(&runtime_opts
, NULL
, 0, &error_abort
);
598 qemu_opts_absorb_qdict(opts
, options
, &local_err
);
600 error_propagate(errp
, local_err
);
604 s
->readahead_size
= qemu_opt_get_size(opts
, CURL_BLOCK_OPT_READAHEAD
,
606 if ((s
->readahead_size
& 0x1ff) != 0) {
607 error_setg(errp
, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512",
612 s
->timeout
= qemu_opt_get_number(opts
, CURL_BLOCK_OPT_TIMEOUT
,
613 CURL_TIMEOUT_DEFAULT
);
614 if (s
->timeout
> CURL_TIMEOUT_MAX
) {
615 error_setg(errp
, "timeout parameter is too large or negative");
619 s
->sslverify
= qemu_opt_get_bool(opts
, CURL_BLOCK_OPT_SSLVERIFY
, true);
621 cookie
= qemu_opt_get(opts
, CURL_BLOCK_OPT_COOKIE
);
622 s
->cookie
= g_strdup(cookie
);
624 file
= qemu_opt_get(opts
, CURL_BLOCK_OPT_URL
);
626 error_setg(errp
, "curl block driver requires an 'url' option");
630 s
->username
= g_strdup(qemu_opt_get(opts
, CURL_BLOCK_OPT_USERNAME
));
631 secretid
= qemu_opt_get(opts
, CURL_BLOCK_OPT_PASSWORD_SECRET
);
634 s
->password
= qcrypto_secret_lookup_as_utf8(secretid
, errp
);
640 s
->proxyusername
= g_strdup(
641 qemu_opt_get(opts
, CURL_BLOCK_OPT_PROXY_USERNAME
));
642 secretid
= qemu_opt_get(opts
, CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET
);
644 s
->proxypassword
= qcrypto_secret_lookup_as_utf8(secretid
, errp
);
645 if (!s
->proxypassword
) {
651 curl_global_init(CURL_GLOBAL_ALL
);
655 DPRINTF("CURL: Opening %s\n", file
);
656 s
->aio_context
= bdrv_get_aio_context(bs
);
657 s
->url
= g_strdup(file
);
658 state
= curl_init_state(bs
, s
);
664 s
->accept_range
= false;
665 curl_easy_setopt(state
->curl
, CURLOPT_NOBODY
, 1);
666 curl_easy_setopt(state
->curl
, CURLOPT_HEADERFUNCTION
,
668 curl_easy_setopt(state
->curl
, CURLOPT_HEADERDATA
, s
);
669 if (curl_easy_perform(state
->curl
))
671 curl_easy_getinfo(state
->curl
, CURLINFO_CONTENT_LENGTH_DOWNLOAD
, &d
);
676 if ((!strncasecmp(s
->url
, "http://", strlen("http://"))
677 || !strncasecmp(s
->url
, "https://", strlen("https://")))
678 && !s
->accept_range
) {
679 pstrcpy(state
->errmsg
, CURL_ERROR_SIZE
,
680 "Server does not support 'range' (byte ranges).");
683 DPRINTF("CURL: Size = %zd\n", s
->len
);
685 curl_clean_state(state
);
686 curl_easy_cleanup(state
->curl
);
689 curl_attach_aio_context(bs
, bdrv_get_aio_context(bs
));
695 error_setg(errp
, "CURL: Error opening file: %s", state
->errmsg
);
696 curl_easy_cleanup(state
->curl
);
705 static const AIOCBInfo curl_aiocb_info
= {
706 .aiocb_size
= sizeof(CURLAIOCB
),
710 static void curl_readv_bh_cb(void *p
)
716 BDRVCURLState
*s
= acb
->common
.bs
->opaque
;
718 qemu_bh_delete(acb
->bh
);
721 size_t start
= acb
->sector_num
* SECTOR_SIZE
;
724 // In case we have the requested data already (e.g. read-ahead),
725 // we can just call the callback and be done.
726 switch (curl_find_buf(s
, start
, acb
->nb_sectors
* SECTOR_SIZE
, acb
)) {
736 // No cache found, so let's start a new request
737 state
= curl_init_state(acb
->common
.bs
, s
);
739 acb
->common
.cb(acb
->common
.opaque
, -EIO
);
745 acb
->end
= (acb
->nb_sectors
* SECTOR_SIZE
);
748 g_free(state
->orig_buf
);
749 state
->buf_start
= start
;
750 state
->buf_len
= acb
->end
+ s
->readahead_size
;
751 end
= MIN(start
+ state
->buf_len
, s
->len
) - 1;
752 state
->orig_buf
= g_try_malloc(state
->buf_len
);
753 if (state
->buf_len
&& state
->orig_buf
== NULL
) {
754 curl_clean_state(state
);
755 acb
->common
.cb(acb
->common
.opaque
, -ENOMEM
);
761 snprintf(state
->range
, 127, "%zd-%zd", start
, end
);
762 DPRINTF("CURL (AIO): Reading %d at %zd (%s)\n",
763 (acb
->nb_sectors
* SECTOR_SIZE
), start
, state
->range
);
764 curl_easy_setopt(state
->curl
, CURLOPT_RANGE
, state
->range
);
766 curl_multi_add_handle(s
->multi
, state
->curl
);
768 /* Tell curl it needs to kick things off */
769 curl_multi_socket_action(s
->multi
, CURL_SOCKET_TIMEOUT
, 0, &running
);
772 static BlockAIOCB
*curl_aio_readv(BlockDriverState
*bs
,
773 int64_t sector_num
, QEMUIOVector
*qiov
, int nb_sectors
,
774 BlockCompletionFunc
*cb
, void *opaque
)
778 acb
= qemu_aio_get(&curl_aiocb_info
, bs
, cb
, opaque
);
781 acb
->sector_num
= sector_num
;
782 acb
->nb_sectors
= nb_sectors
;
784 acb
->bh
= aio_bh_new(bdrv_get_aio_context(bs
), curl_readv_bh_cb
, acb
);
785 qemu_bh_schedule(acb
->bh
);
789 static void curl_close(BlockDriverState
*bs
)
791 BDRVCURLState
*s
= bs
->opaque
;
793 DPRINTF("CURL: Close\n");
794 curl_detach_aio_context(bs
);
800 static int64_t curl_getlength(BlockDriverState
*bs
)
802 BDRVCURLState
*s
= bs
->opaque
;
806 static BlockDriver bdrv_http
= {
807 .format_name
= "http",
808 .protocol_name
= "http",
810 .instance_size
= sizeof(BDRVCURLState
),
811 .bdrv_parse_filename
= curl_parse_filename
,
812 .bdrv_file_open
= curl_open
,
813 .bdrv_close
= curl_close
,
814 .bdrv_getlength
= curl_getlength
,
816 .bdrv_aio_readv
= curl_aio_readv
,
818 .bdrv_detach_aio_context
= curl_detach_aio_context
,
819 .bdrv_attach_aio_context
= curl_attach_aio_context
,
822 static BlockDriver bdrv_https
= {
823 .format_name
= "https",
824 .protocol_name
= "https",
826 .instance_size
= sizeof(BDRVCURLState
),
827 .bdrv_parse_filename
= curl_parse_filename
,
828 .bdrv_file_open
= curl_open
,
829 .bdrv_close
= curl_close
,
830 .bdrv_getlength
= curl_getlength
,
832 .bdrv_aio_readv
= curl_aio_readv
,
834 .bdrv_detach_aio_context
= curl_detach_aio_context
,
835 .bdrv_attach_aio_context
= curl_attach_aio_context
,
838 static BlockDriver bdrv_ftp
= {
839 .format_name
= "ftp",
840 .protocol_name
= "ftp",
842 .instance_size
= sizeof(BDRVCURLState
),
843 .bdrv_parse_filename
= curl_parse_filename
,
844 .bdrv_file_open
= curl_open
,
845 .bdrv_close
= curl_close
,
846 .bdrv_getlength
= curl_getlength
,
848 .bdrv_aio_readv
= curl_aio_readv
,
850 .bdrv_detach_aio_context
= curl_detach_aio_context
,
851 .bdrv_attach_aio_context
= curl_attach_aio_context
,
854 static BlockDriver bdrv_ftps
= {
855 .format_name
= "ftps",
856 .protocol_name
= "ftps",
858 .instance_size
= sizeof(BDRVCURLState
),
859 .bdrv_parse_filename
= curl_parse_filename
,
860 .bdrv_file_open
= curl_open
,
861 .bdrv_close
= curl_close
,
862 .bdrv_getlength
= curl_getlength
,
864 .bdrv_aio_readv
= curl_aio_readv
,
866 .bdrv_detach_aio_context
= curl_detach_aio_context
,
867 .bdrv_attach_aio_context
= curl_attach_aio_context
,
870 static BlockDriver bdrv_tftp
= {
871 .format_name
= "tftp",
872 .protocol_name
= "tftp",
874 .instance_size
= sizeof(BDRVCURLState
),
875 .bdrv_parse_filename
= curl_parse_filename
,
876 .bdrv_file_open
= curl_open
,
877 .bdrv_close
= curl_close
,
878 .bdrv_getlength
= curl_getlength
,
880 .bdrv_aio_readv
= curl_aio_readv
,
882 .bdrv_detach_aio_context
= curl_detach_aio_context
,
883 .bdrv_attach_aio_context
= curl_attach_aio_context
,
886 static void curl_block_init(void)
888 bdrv_register(&bdrv_http
);
889 bdrv_register(&bdrv_https
);
890 bdrv_register(&bdrv_ftp
);
891 bdrv_register(&bdrv_ftps
);
892 bdrv_register(&bdrv_tftp
);
895 block_init(curl_block_init
);