Lines Matching +full:multi +full:- +full:block
2 * QEMU Block driver for CURL images
27 #include "qemu/error-report.h"
30 #include "block/block-io.h"
31 #include "block/block_int.h"
60 #define CURL_BLOCK_OPT_COOKIE_SECRET "cookie-secret"
62 #define CURL_BLOCK_OPT_PASSWORD_SECRET "password-secret"
63 #define CURL_BLOCK_OPT_PROXY_USERNAME "proxy-username"
64 #define CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET "proxy-password-secret"
107 CURLM *multi; member
111 GHashTable *sockets; /* GINT_TO_POINTER(fd) -> socket */
133 BDRVCURLState *s = socket->s; in curl_drop_socket()
135 aio_set_fd_handler(s->aio_context, socket->fd, in curl_drop_socket()
145 /* Called from curl_multi_do_locked, with s->mutex held. */
146 static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) in curl_timer_cb() argument
151 if (timeout_ms == -1) { in curl_timer_cb()
152 timer_del(&s->timer); in curl_timer_cb()
155 timer_mod(&s->timer, in curl_timer_cb()
161 /* Called from curl_multi_do_locked, with s->mutex held. */
170 s = state->s; in curl_sock_cb()
172 socket = g_hash_table_lookup(s->sockets, GINT_TO_POINTER(fd)); in curl_sock_cb()
175 socket->fd = fd; in curl_sock_cb()
176 socket->s = s; in curl_sock_cb()
177 g_hash_table_insert(s->sockets, GINT_TO_POINTER(fd), socket); in curl_sock_cb()
183 aio_set_fd_handler(s->aio_context, fd, in curl_sock_cb()
187 aio_set_fd_handler(s->aio_context, fd, in curl_sock_cb()
191 aio_set_fd_handler(s->aio_context, fd, in curl_sock_cb()
196 aio_set_fd_handler(s->aio_context, fd, in curl_sock_cb()
202 g_hash_table_remove(s->sockets, GINT_TO_POINTER(fd)); in curl_sock_cb()
208 /* Called from curl_multi_do_locked, with s->mutex held. */
215 const char *t = "accept-ranges : bytes "; /* A lowercase template */ in curl_header_cb()
233 s->accept_range = true; in curl_header_cb()
239 /* Called from curl_multi_do_locked, with s->mutex held. */
247 if (!s || !s->orig_buf) { in curl_read_cb()
251 if (s->buf_off >= s->buf_len) { in curl_read_cb()
255 realsize = MIN(realsize, s->buf_len - s->buf_off); in curl_read_cb()
256 memcpy(s->orig_buf + s->buf_off, ptr, realsize); in curl_read_cb()
257 s->buf_off += realsize; in curl_read_cb()
264 /* Called with s->mutex held. */
270 uint64_t clamped_end = MIN(end, s->len); in curl_find_buf()
271 uint64_t clamped_len = clamped_end - start; in curl_find_buf()
274 CURLState *state = &s->states[i]; in curl_find_buf()
275 uint64_t buf_end = (state->buf_start + state->buf_off); in curl_find_buf()
276 uint64_t buf_fend = (state->buf_start + state->buf_len); in curl_find_buf()
278 if (!state->orig_buf) in curl_find_buf()
280 if (!state->buf_off) in curl_find_buf()
284 if ((start >= state->buf_start) && in curl_find_buf()
286 (clamped_end >= state->buf_start) && in curl_find_buf()
289 char *buf = state->orig_buf + (start - state->buf_start); in curl_find_buf()
291 qemu_iovec_from_buf(acb->qiov, 0, buf, clamped_len); in curl_find_buf()
293 qemu_iovec_memset(acb->qiov, clamped_len, 0, len - clamped_len); in curl_find_buf()
295 acb->ret = 0; in curl_find_buf()
300 if (state->in_use && in curl_find_buf()
301 (start >= state->buf_start) && in curl_find_buf()
303 (clamped_end >= state->buf_start) && in curl_find_buf()
308 acb->start = start - state->buf_start; in curl_find_buf()
309 acb->end = acb->start + clamped_len; in curl_find_buf()
312 if (!state->acb[j]) { in curl_find_buf()
313 state->acb[j] = acb; in curl_find_buf()
323 /* Called with s->mutex held. */
332 msg = curl_multi_info_read(s->multi, &msgs_in_queue); in curl_multi_check_completion()
338 if (msg->msg == CURLMSG_DONE) { in curl_multi_check_completion()
341 bool error = msg->data.result != CURLE_OK; in curl_multi_check_completion()
343 curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, in curl_multi_check_completion()
353 error_report("curl: %s", state->errmsg); in curl_multi_check_completion()
354 if (--errcount == 0) { in curl_multi_check_completion()
361 CURLAIOCB *acb = state->acb[i]; in curl_multi_check_completion()
369 assert(state->buf_off >= acb->end); in curl_multi_check_completion()
371 qemu_iovec_from_buf(acb->qiov, 0, in curl_multi_check_completion()
372 state->orig_buf + acb->start, in curl_multi_check_completion()
373 acb->end - acb->start); in curl_multi_check_completion()
375 if (acb->end - acb->start < acb->bytes) { in curl_multi_check_completion()
376 size_t offset = acb->end - acb->start; in curl_multi_check_completion()
377 qemu_iovec_memset(acb->qiov, offset, 0, in curl_multi_check_completion()
378 acb->bytes - offset); in curl_multi_check_completion()
382 acb->ret = error ? -EIO : 0; in curl_multi_check_completion()
383 state->acb[i] = NULL; in curl_multi_check_completion()
384 qemu_mutex_unlock(&s->mutex); in curl_multi_check_completion()
385 aio_co_wake(acb->co); in curl_multi_check_completion()
386 qemu_mutex_lock(&s->mutex); in curl_multi_check_completion()
395 /* Called with s->mutex held. */
398 BDRVCURLState *s = socket->s; in curl_multi_do_locked()
402 if (!s->multi) { in curl_multi_do_locked()
407 r = curl_multi_socket_action(s->multi, socket->fd, 0, &running); in curl_multi_do_locked()
414 BDRVCURLState *s = socket->s; in curl_multi_do()
416 qemu_mutex_lock(&s->mutex); in curl_multi_do()
419 qemu_mutex_unlock(&s->mutex); in curl_multi_do()
427 if (!s->multi) { in curl_multi_timeout_do()
431 qemu_mutex_lock(&s->mutex); in curl_multi_timeout_do()
432 curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running); in curl_multi_timeout_do()
435 qemu_mutex_unlock(&s->mutex); in curl_multi_timeout_do()
438 /* Called with s->mutex held. */
445 if (!s->states[i].in_use) { in curl_find_state()
446 state = &s->states[i]; in curl_find_state()
447 state->in_use = 1; in curl_find_state()
456 if (!state->curl) { in curl_init_state()
457 state->curl = curl_easy_init(); in curl_init_state()
458 if (!state->curl) { in curl_init_state()
459 return -EIO; in curl_init_state()
461 if (curl_easy_setopt(state->curl, CURLOPT_URL, s->url) || in curl_init_state()
462 curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER, in curl_init_state()
463 (long) s->sslverify) || in curl_init_state()
464 curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYHOST, in curl_init_state()
465 s->sslverify ? 2L : 0L)) { in curl_init_state()
468 if (s->cookie) { in curl_init_state()
469 if (curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie)) { in curl_init_state()
473 if (curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, (long)s->timeout) || in curl_init_state()
474 curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, in curl_init_state()
476 curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state) || in curl_init_state()
477 curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state) || in curl_init_state()
478 curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1) || in curl_init_state()
479 curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1) || in curl_init_state()
480 curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1) || in curl_init_state()
481 curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg) || in curl_init_state()
482 curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1)) { in curl_init_state()
485 if (s->username) { in curl_init_state()
486 if (curl_easy_setopt(state->curl, CURLOPT_USERNAME, s->username)) { in curl_init_state()
490 if (s->password) { in curl_init_state()
491 if (curl_easy_setopt(state->curl, CURLOPT_PASSWORD, s->password)) { in curl_init_state()
495 if (s->proxyusername) { in curl_init_state()
496 if (curl_easy_setopt(state->curl, in curl_init_state()
497 CURLOPT_PROXYUSERNAME, s->proxyusername)) { in curl_init_state()
501 if (s->proxypassword) { in curl_init_state()
502 if (curl_easy_setopt(state->curl, in curl_init_state()
503 CURLOPT_PROXYPASSWORD, s->proxypassword)) { in curl_init_state()
510 * CVE-2013-0249. in curl_init_state()
517 if (curl_easy_setopt(state->curl, in curl_init_state()
519 curl_easy_setopt(state->curl, in curl_init_state()
524 if (curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS) || in curl_init_state()
525 curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS)) { in curl_init_state()
531 if (curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1)) { in curl_init_state()
537 state->s = s; in curl_init_state()
542 curl_easy_cleanup(state->curl); in curl_init_state()
543 state->curl = NULL; in curl_init_state()
544 return -EIO; in curl_init_state()
547 /* Called with s->mutex held. */
552 assert(!s->acb[j]); in curl_clean_state()
555 if (s->s->multi) in curl_clean_state()
556 curl_multi_remove_handle(s->s->multi, s->curl); in curl_clean_state()
558 s->in_use = 0; in curl_clean_state()
560 qemu_co_enter_next(&s->s->free_state_waitq, &s->s->mutex); in curl_clean_state()
571 BDRVCURLState *s = bs->opaque; in curl_detach_aio_context()
574 WITH_QEMU_LOCK_GUARD(&s->mutex) { in curl_detach_aio_context()
575 curl_drop_all_sockets(s->sockets); in curl_detach_aio_context()
577 if (s->states[i].in_use) { in curl_detach_aio_context()
578 curl_clean_state(&s->states[i]); in curl_detach_aio_context()
580 if (s->states[i].curl) { in curl_detach_aio_context()
581 curl_easy_cleanup(s->states[i].curl); in curl_detach_aio_context()
582 s->states[i].curl = NULL; in curl_detach_aio_context()
584 g_free(s->states[i].orig_buf); in curl_detach_aio_context()
585 s->states[i].orig_buf = NULL; in curl_detach_aio_context()
587 if (s->multi) { in curl_detach_aio_context()
588 curl_multi_cleanup(s->multi); in curl_detach_aio_context()
589 s->multi = NULL; in curl_detach_aio_context()
593 timer_del(&s->timer); in curl_detach_aio_context()
599 BDRVCURLState *s = bs->opaque; in curl_attach_aio_context()
601 aio_timer_init(new_context, &s->timer, in curl_attach_aio_context()
605 assert(!s->multi); in curl_attach_aio_context()
606 s->multi = curl_multi_init(); in curl_attach_aio_context()
607 s->aio_context = new_context; in curl_attach_aio_context()
608 curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb); in curl_attach_aio_context()
609 curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s); in curl_attach_aio_context()
610 curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb); in curl_attach_aio_context()
675 BDRVCURLState *s = bs->opaque; in curl_open()
703 return -EIO; in curl_open()
708 qemu_mutex_init(&s->mutex); in curl_open()
714 s->readahead_size = qemu_opt_get_size(opts, CURL_BLOCK_OPT_READAHEAD, in curl_open()
716 if ((s->readahead_size & 0x1ff) != 0) { in curl_open()
718 s->readahead_size); in curl_open()
722 s->timeout = qemu_opt_get_number(opts, CURL_BLOCK_OPT_TIMEOUT, in curl_open()
724 if (s->timeout > CURL_TIMEOUT_MAX) { in curl_open()
729 s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, in curl_open()
742 s->cookie = qcrypto_secret_lookup_as_utf8(cookie_secret, errp); in curl_open()
743 if (!s->cookie) { in curl_open()
747 s->cookie = g_strdup(cookie); in curl_open()
752 error_setg(errp, "curl block driver requires an 'url' option"); in curl_open()
756 if (!strstart(file, bs->drv->protocol_name, &protocol_delimiter) || in curl_open()
760 "start with '%s://')", bs->drv->protocol_name, file, in curl_open()
761 bs->drv->protocol_name); in curl_open()
765 s->username = g_strdup(qemu_opt_get(opts, CURL_BLOCK_OPT_USERNAME)); in curl_open()
769 s->password = qcrypto_secret_lookup_as_utf8(secretid, errp); in curl_open()
770 if (!s->password) { in curl_open()
775 s->proxyusername = g_strdup( in curl_open()
779 s->proxypassword = qcrypto_secret_lookup_as_utf8(secretid, errp); in curl_open()
780 if (!s->proxypassword) { in curl_open()
786 qemu_co_queue_init(&s->free_state_waitq); in curl_open()
787 s->aio_context = bdrv_get_aio_context(bs); in curl_open()
788 s->url = g_strdup(file); in curl_open()
789 s->sockets = g_hash_table_new_full(NULL, NULL, NULL, g_free); in curl_open()
790 qemu_mutex_lock(&s->mutex); in curl_open()
792 qemu_mutex_unlock(&s->mutex); in curl_open()
800 pstrcpy(state->errmsg, CURL_ERROR_SIZE, in curl_open()
805 s->accept_range = false; in curl_open()
806 if (curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1) || in curl_open()
807 curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION, curl_header_cb) || in curl_open()
808 curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s)) { in curl_open()
809 pstrcpy(state->errmsg, CURL_ERROR_SIZE, in curl_open()
813 if (curl_easy_perform(state->curl)) in curl_open()
819 if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &cl)) { in curl_open()
823 if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &cl)) { in curl_open()
828 * know or the size is zero. From 7.19.4 CURL returns -1 if size is not in curl_open()
829 * known and zero if it is really zero-length file. */ in curl_open()
832 pstrcpy(state->errmsg, CURL_ERROR_SIZE, in curl_open()
838 pstrcpy(state->errmsg, CURL_ERROR_SIZE, in curl_open()
839 "Unknown file size or zero-length file."); in curl_open()
844 s->len = cl; in curl_open()
846 if ((!strncasecmp(s->url, "http://", strlen("http://")) in curl_open()
847 || !strncasecmp(s->url, "https://", strlen("https://"))) in curl_open()
848 && !s->accept_range) { in curl_open()
849 pstrcpy(state->errmsg, CURL_ERROR_SIZE, in curl_open()
853 trace_curl_open_size(s->len); in curl_open()
855 qemu_mutex_lock(&s->mutex); in curl_open()
857 qemu_mutex_unlock(&s->mutex); in curl_open()
858 curl_easy_cleanup(state->curl); in curl_open()
859 state->curl = NULL; in curl_open()
867 error_setg(errp, "CURL: Error opening file: %s", state->errmsg); in curl_open()
868 curl_easy_cleanup(state->curl); in curl_open()
869 state->curl = NULL; in curl_open()
871 qemu_mutex_destroy(&s->mutex); in curl_open()
872 g_free(s->cookie); in curl_open()
873 g_free(s->url); in curl_open()
874 g_free(s->username); in curl_open()
875 g_free(s->proxyusername); in curl_open()
876 g_free(s->proxypassword); in curl_open()
877 if (s->sockets) { in curl_open()
878 curl_drop_all_sockets(s->sockets); in curl_open()
879 g_hash_table_destroy(s->sockets); in curl_open()
882 return -EINVAL; in curl_open()
890 BDRVCURLState *s = bs->opaque; in curl_setup_preadv()
892 uint64_t start = acb->offset; in curl_setup_preadv()
895 qemu_mutex_lock(&s->mutex); in curl_setup_preadv()
897 // In case we have the requested data already (e.g. read-ahead), in curl_setup_preadv()
899 if (curl_find_buf(s, start, acb->bytes, acb)) { in curl_setup_preadv()
909 qemu_co_queue_wait(&s->free_state_waitq, &s->mutex); in curl_setup_preadv()
914 acb->ret = -EIO; in curl_setup_preadv()
918 acb->start = 0; in curl_setup_preadv()
919 acb->end = MIN(acb->bytes, s->len - start); in curl_setup_preadv()
921 state->buf_off = 0; in curl_setup_preadv()
922 g_free(state->orig_buf); in curl_setup_preadv()
923 state->buf_start = start; in curl_setup_preadv()
924 state->buf_len = MIN(acb->end + s->readahead_size, s->len - start); in curl_setup_preadv()
925 end = start + state->buf_len - 1; in curl_setup_preadv()
926 state->orig_buf = g_try_malloc(state->buf_len); in curl_setup_preadv()
927 if (state->buf_len && state->orig_buf == NULL) { in curl_setup_preadv()
929 acb->ret = -ENOMEM; in curl_setup_preadv()
932 state->acb[0] = acb; in curl_setup_preadv()
934 snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end); in curl_setup_preadv()
935 trace_curl_setup_preadv(acb->bytes, start, state->range); in curl_setup_preadv()
936 if (curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range) || in curl_setup_preadv()
937 curl_multi_add_handle(s->multi, state->curl) != CURLM_OK) { in curl_setup_preadv()
938 state->acb[0] = NULL; in curl_setup_preadv()
939 acb->ret = -EIO; in curl_setup_preadv()
946 curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running); in curl_setup_preadv()
949 qemu_mutex_unlock(&s->mutex); in curl_setup_preadv()
958 .ret = -EINPROGRESS, in curl_co_preadv()
965 while (acb.ret == -EINPROGRESS) { in curl_co_preadv()
973 BDRVCURLState *s = bs->opaque; in curl_close()
977 qemu_mutex_destroy(&s->mutex); in curl_close()
979 g_hash_table_destroy(s->sockets); in curl_close()
980 g_free(s->cookie); in curl_close()
981 g_free(s->url); in curl_close()
982 g_free(s->username); in curl_close()
983 g_free(s->proxyusername); in curl_close()
984 g_free(s->proxypassword); in curl_close()
989 BDRVCURLState *s = bs->opaque; in curl_co_getlength()
990 return s->len; in curl_co_getlength()
995 BDRVCURLState *s = bs->opaque; in curl_refresh_filename()
997 /* "readahead" and "timeout" do not change the guest-visible data, in curl_refresh_filename()
999 if (s->sslverify != CURL_BLOCK_OPT_SSLVERIFY_DEFAULT || in curl_refresh_filename()
1000 s->cookie || s->username || s->password || s->proxyusername || in curl_refresh_filename()
1001 s->proxypassword) in curl_refresh_filename()
1006 pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), s->url); in curl_refresh_filename()