1769ce76dSAlexander Graf /* 2769ce76dSAlexander Graf * QEMU Block driver for CURL images 3769ce76dSAlexander Graf * 4769ce76dSAlexander Graf * Copyright (c) 2009 Alexander Graf <agraf@suse.de> 5769ce76dSAlexander Graf * 6769ce76dSAlexander Graf * Permission is hereby granted, free of charge, to any person obtaining a copy 7769ce76dSAlexander Graf * of this software and associated documentation files (the "Software"), to deal 8769ce76dSAlexander Graf * in the Software without restriction, including without limitation the rights 9769ce76dSAlexander Graf * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10769ce76dSAlexander Graf * copies of the Software, and to permit persons to whom the Software is 11769ce76dSAlexander Graf * furnished to do so, subject to the following conditions: 12769ce76dSAlexander Graf * 13769ce76dSAlexander Graf * The above copyright notice and this permission notice shall be included in 14769ce76dSAlexander Graf * all copies or substantial portions of the Software. 15769ce76dSAlexander Graf * 16769ce76dSAlexander Graf * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17769ce76dSAlexander Graf * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18769ce76dSAlexander Graf * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19769ce76dSAlexander Graf * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20769ce76dSAlexander Graf * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21769ce76dSAlexander Graf * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22769ce76dSAlexander Graf * THE SOFTWARE. 23769ce76dSAlexander Graf */ 2480c71a24SPeter Maydell #include "qemu/osdep.h" 25da34e65cSMarkus Armbruster #include "qapi/error.h" 26769ce76dSAlexander Graf #include "qemu-common.h" 27796a060bSRichard W.M. Jones #include "qemu/error-report.h" 28737e150eSPaolo Bonzini #include "block/block_int.h" 2997a3ea57SMatthew Booth #include "qapi/qmp/qbool.h" 30d49b6836SMarkus Armbruster #include "qapi/qmp/qstring.h" 311bff9606SDaniel P. Berrange #include "crypto/secret.h" 32769ce76dSAlexander Graf #include <curl/curl.h> 33f348b6d1SVeronia Bahaa #include "qemu/cutils.h" 34769ce76dSAlexander Graf 3541c23467SRichard W.M. Jones // #define DEBUG_CURL 36769ce76dSAlexander Graf // #define DEBUG_VERBOSE 37769ce76dSAlexander Graf 38769ce76dSAlexander Graf #ifdef DEBUG_CURL 39ed79f37dSZhou Jie #define DEBUG_CURL_PRINT 1 40769ce76dSAlexander Graf #else 41ed79f37dSZhou Jie #define DEBUG_CURL_PRINT 0 42769ce76dSAlexander Graf #endif 43ed79f37dSZhou Jie #define DPRINTF(fmt, ...) \ 44ed79f37dSZhou Jie do { \ 45ed79f37dSZhou Jie if (DEBUG_CURL_PRINT) { \ 46ed79f37dSZhou Jie fprintf(stderr, fmt, ## __VA_ARGS__); \ 47ed79f37dSZhou Jie } \ 48ed79f37dSZhou Jie } while (0) 49769ce76dSAlexander Graf 50031fd1beSPeter Maydell #if LIBCURL_VERSION_NUM >= 0x071000 51031fd1beSPeter Maydell /* The multi interface timer callback was introduced in 7.16.0 */ 52031fd1beSPeter Maydell #define NEED_CURL_TIMER_CALLBACK 539aedd5a5SMatthew Booth #define HAVE_SOCKET_ACTION 549aedd5a5SMatthew Booth #endif 559aedd5a5SMatthew Booth 569aedd5a5SMatthew Booth #ifndef HAVE_SOCKET_ACTION 579aedd5a5SMatthew Booth /* If curl_multi_socket_action isn't available, define it statically here in 589aedd5a5SMatthew Booth * terms of curl_multi_socket. Note that ev_bitmask will be ignored, which is 599aedd5a5SMatthew Booth * less efficient but still safe. */ 609aedd5a5SMatthew Booth static CURLMcode __curl_multi_socket_action(CURLM *multi_handle, 619aedd5a5SMatthew Booth curl_socket_t sockfd, 629aedd5a5SMatthew Booth int ev_bitmask, 639aedd5a5SMatthew Booth int *running_handles) 649aedd5a5SMatthew Booth { 659aedd5a5SMatthew Booth return curl_multi_socket(multi_handle, sockfd, running_handles); 669aedd5a5SMatthew Booth } 679aedd5a5SMatthew Booth #define curl_multi_socket_action __curl_multi_socket_action 68031fd1beSPeter Maydell #endif 69031fd1beSPeter Maydell 70fb6d1bbdSStefan Hajnoczi #define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \ 7123dce387SMax Reitz CURLPROTO_FTP | CURLPROTO_FTPS) 72fb6d1bbdSStefan Hajnoczi 73769ce76dSAlexander Graf #define CURL_NUM_STATES 8 74769ce76dSAlexander Graf #define CURL_NUM_ACB 8 75e3542c67SMatthew Booth #define READ_AHEAD_DEFAULT (256 * 1024) 76212aefaaSDaniel Henrique Barboza #define CURL_TIMEOUT_DEFAULT 5 77f76faedaSRichard W.M. Jones #define CURL_TIMEOUT_MAX 10000 78769ce76dSAlexander Graf 79769ce76dSAlexander Graf #define FIND_RET_NONE 0 80769ce76dSAlexander Graf #define FIND_RET_OK 1 81769ce76dSAlexander Graf #define FIND_RET_WAIT 2 82769ce76dSAlexander Graf 83e3542c67SMatthew Booth #define CURL_BLOCK_OPT_URL "url" 84e3542c67SMatthew Booth #define CURL_BLOCK_OPT_READAHEAD "readahead" 8597a3ea57SMatthew Booth #define CURL_BLOCK_OPT_SSLVERIFY "sslverify" 86212aefaaSDaniel Henrique Barboza #define CURL_BLOCK_OPT_TIMEOUT "timeout" 87a94f83d9SRichard W.M. Jones #define CURL_BLOCK_OPT_COOKIE "cookie" 881bff9606SDaniel P. Berrange #define CURL_BLOCK_OPT_USERNAME "username" 891bff9606SDaniel P. Berrange #define CURL_BLOCK_OPT_PASSWORD_SECRET "password-secret" 901bff9606SDaniel P. Berrange #define CURL_BLOCK_OPT_PROXY_USERNAME "proxy-username" 911bff9606SDaniel P. Berrange #define CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET "proxy-password-secret" 92e3542c67SMatthew Booth 93769ce76dSAlexander Graf struct BDRVCURLState; 94769ce76dSAlexander Graf 95769ce76dSAlexander Graf typedef struct CURLAIOCB { 967c84b1b8SMarkus Armbruster BlockAIOCB common; 97769ce76dSAlexander Graf QEMUIOVector *qiov; 98363c3c85SNick Thomas 99363c3c85SNick Thomas int64_t sector_num; 100363c3c85SNick Thomas int nb_sectors; 101363c3c85SNick Thomas 102769ce76dSAlexander Graf size_t start; 103769ce76dSAlexander Graf size_t end; 104769ce76dSAlexander Graf } CURLAIOCB; 105769ce76dSAlexander Graf 106ff5ca166SMax Reitz typedef struct CURLSocket { 107ff5ca166SMax Reitz int fd; 108ff5ca166SMax Reitz QLIST_ENTRY(CURLSocket) next; 109ff5ca166SMax Reitz } CURLSocket; 110ff5ca166SMax Reitz 111769ce76dSAlexander Graf typedef struct CURLState 112769ce76dSAlexander Graf { 113769ce76dSAlexander Graf struct BDRVCURLState *s; 114769ce76dSAlexander Graf CURLAIOCB *acb[CURL_NUM_ACB]; 115769ce76dSAlexander Graf CURL *curl; 116ff5ca166SMax Reitz QLIST_HEAD(, CURLSocket) sockets; 117769ce76dSAlexander Graf char *orig_buf; 118769ce76dSAlexander Graf size_t buf_start; 119769ce76dSAlexander Graf size_t buf_off; 120769ce76dSAlexander Graf size_t buf_len; 121769ce76dSAlexander Graf char range[128]; 122769ce76dSAlexander Graf char errmsg[CURL_ERROR_SIZE]; 123769ce76dSAlexander Graf char in_use; 124769ce76dSAlexander Graf } CURLState; 125769ce76dSAlexander Graf 126769ce76dSAlexander Graf typedef struct BDRVCURLState { 127769ce76dSAlexander Graf CURLM *multi; 128031fd1beSPeter Maydell QEMUTimer timer; 129769ce76dSAlexander Graf size_t len; 130769ce76dSAlexander Graf CURLState states[CURL_NUM_STATES]; 131769ce76dSAlexander Graf char *url; 132c76f4952SNolan size_t readahead_size; 13397a3ea57SMatthew Booth bool sslverify; 134f76faedaSRichard W.M. Jones uint64_t timeout; 135a94f83d9SRichard W.M. Jones char *cookie; 1363494d650SFam Zheng bool accept_range; 13763f0f45fSStefan Hajnoczi AioContext *aio_context; 1381bff9606SDaniel P. Berrange char *username; 1391bff9606SDaniel P. Berrange char *password; 1401bff9606SDaniel P. Berrange char *proxyusername; 1411bff9606SDaniel P. Berrange char *proxypassword; 142769ce76dSAlexander Graf } BDRVCURLState; 143769ce76dSAlexander Graf 144769ce76dSAlexander Graf static void curl_clean_state(CURLState *s); 145769ce76dSAlexander Graf static void curl_multi_do(void *arg); 146838ef602SMatthew Booth static void curl_multi_read(void *arg); 147769ce76dSAlexander Graf 148031fd1beSPeter Maydell #ifdef NEED_CURL_TIMER_CALLBACK 149031fd1beSPeter Maydell static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) 150031fd1beSPeter Maydell { 151031fd1beSPeter Maydell BDRVCURLState *s = opaque; 152031fd1beSPeter Maydell 153031fd1beSPeter Maydell DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms); 154031fd1beSPeter Maydell if (timeout_ms == -1) { 155031fd1beSPeter Maydell timer_del(&s->timer); 156031fd1beSPeter Maydell } else { 157031fd1beSPeter Maydell int64_t timeout_ns = (int64_t)timeout_ms * 1000 * 1000; 158031fd1beSPeter Maydell timer_mod(&s->timer, 159031fd1beSPeter Maydell qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + timeout_ns); 160031fd1beSPeter Maydell } 161031fd1beSPeter Maydell return 0; 162031fd1beSPeter Maydell } 163031fd1beSPeter Maydell #endif 164031fd1beSPeter Maydell 165769ce76dSAlexander Graf static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, 16663f0f45fSStefan Hajnoczi void *userp, void *sp) 167769ce76dSAlexander Graf { 16863f0f45fSStefan Hajnoczi BDRVCURLState *s; 169838ef602SMatthew Booth CURLState *state = NULL; 170ff5ca166SMax Reitz CURLSocket *socket; 171ff5ca166SMax Reitz 172838ef602SMatthew Booth curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&state); 17363f0f45fSStefan Hajnoczi s = state->s; 174838ef602SMatthew Booth 175ff5ca166SMax Reitz QLIST_FOREACH(socket, &state->sockets, next) { 176ff5ca166SMax Reitz if (socket->fd == fd) { 177ff5ca166SMax Reitz if (action == CURL_POLL_REMOVE) { 178ff5ca166SMax Reitz QLIST_REMOVE(socket, next); 179ff5ca166SMax Reitz g_free(socket); 180ff5ca166SMax Reitz } 181ff5ca166SMax Reitz break; 182ff5ca166SMax Reitz } 183ff5ca166SMax Reitz } 184ff5ca166SMax Reitz if (!socket) { 185ff5ca166SMax Reitz socket = g_new0(CURLSocket, 1); 186ff5ca166SMax Reitz socket->fd = fd; 187ff5ca166SMax Reitz QLIST_INSERT_HEAD(&state->sockets, socket, next); 188ff5ca166SMax Reitz } 189ff5ca166SMax Reitz socket = NULL; 190ff5ca166SMax Reitz 19192b6a160SFam Zheng DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, (int)fd); 192769ce76dSAlexander Graf switch (action) { 193769ce76dSAlexander Graf case CURL_POLL_IN: 194dca21ef2SFam Zheng aio_set_fd_handler(s->aio_context, fd, false, 195dca21ef2SFam Zheng curl_multi_read, NULL, state); 196769ce76dSAlexander Graf break; 197769ce76dSAlexander Graf case CURL_POLL_OUT: 198dca21ef2SFam Zheng aio_set_fd_handler(s->aio_context, fd, false, 199dca21ef2SFam Zheng NULL, curl_multi_do, state); 200769ce76dSAlexander Graf break; 201769ce76dSAlexander Graf case CURL_POLL_INOUT: 202dca21ef2SFam Zheng aio_set_fd_handler(s->aio_context, fd, false, 203dca21ef2SFam Zheng curl_multi_read, curl_multi_do, state); 204769ce76dSAlexander Graf break; 205769ce76dSAlexander Graf case CURL_POLL_REMOVE: 206dca21ef2SFam Zheng aio_set_fd_handler(s->aio_context, fd, false, 207dca21ef2SFam Zheng NULL, NULL, NULL); 208769ce76dSAlexander Graf break; 209769ce76dSAlexander Graf } 210769ce76dSAlexander Graf 211769ce76dSAlexander Graf return 0; 212769ce76dSAlexander Graf } 213769ce76dSAlexander Graf 2143494d650SFam Zheng static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque) 215769ce76dSAlexander Graf { 2163494d650SFam Zheng BDRVCURLState *s = opaque; 217769ce76dSAlexander Graf size_t realsize = size * nmemb; 2183494d650SFam Zheng const char *accept_line = "Accept-Ranges: bytes"; 219769ce76dSAlexander Graf 2203494d650SFam Zheng if (realsize >= strlen(accept_line) 2213494d650SFam Zheng && strncmp((char *)ptr, accept_line, strlen(accept_line)) == 0) { 2223494d650SFam Zheng s->accept_range = true; 2230bfcd599SBlue Swirl } 224769ce76dSAlexander Graf 225769ce76dSAlexander Graf return realsize; 226769ce76dSAlexander Graf } 227769ce76dSAlexander Graf 228769ce76dSAlexander Graf static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque) 229769ce76dSAlexander Graf { 230769ce76dSAlexander Graf CURLState *s = ((CURLState*)opaque); 231769ce76dSAlexander Graf size_t realsize = size * nmemb; 232769ce76dSAlexander Graf int i; 233769ce76dSAlexander Graf 2340bfcd599SBlue Swirl DPRINTF("CURL: Just reading %zd bytes\n", realsize); 235769ce76dSAlexander Graf 2364e767657SMax Reitz if (!s || !s->orig_buf) { 2374e767657SMax Reitz goto read_end; 2384e767657SMax Reitz } 239769ce76dSAlexander Graf 2406d4b9e55SFam Zheng if (s->buf_off >= s->buf_len) { 2416d4b9e55SFam Zheng /* buffer full, read nothing */ 2424e767657SMax Reitz goto read_end; 2436d4b9e55SFam Zheng } 2446d4b9e55SFam Zheng realsize = MIN(realsize, s->buf_len - s->buf_off); 245769ce76dSAlexander Graf memcpy(s->orig_buf + s->buf_off, ptr, realsize); 246769ce76dSAlexander Graf s->buf_off += realsize; 247769ce76dSAlexander Graf 248769ce76dSAlexander Graf for(i=0; i<CURL_NUM_ACB; i++) { 249769ce76dSAlexander Graf CURLAIOCB *acb = s->acb[i]; 250769ce76dSAlexander Graf 251769ce76dSAlexander Graf if (!acb) 252769ce76dSAlexander Graf continue; 253769ce76dSAlexander Graf 254769ce76dSAlexander Graf if ((s->buf_off >= acb->end)) { 255*4e504535SMax Reitz size_t request_length = acb->nb_sectors * BDRV_SECTOR_SIZE; 256*4e504535SMax Reitz 25703396148SMichael Tokarev qemu_iovec_from_buf(acb->qiov, 0, s->orig_buf + acb->start, 258769ce76dSAlexander Graf acb->end - acb->start); 259*4e504535SMax Reitz 260*4e504535SMax Reitz if (acb->end - acb->start < request_length) { 261*4e504535SMax Reitz size_t offset = acb->end - acb->start; 262*4e504535SMax Reitz qemu_iovec_memset(acb->qiov, offset, 0, 263*4e504535SMax Reitz request_length - offset); 264*4e504535SMax Reitz } 265*4e504535SMax Reitz 266769ce76dSAlexander Graf acb->common.cb(acb->common.opaque, 0); 2678007429aSFam Zheng qemu_aio_unref(acb); 268769ce76dSAlexander Graf s->acb[i] = NULL; 269769ce76dSAlexander Graf } 270769ce76dSAlexander Graf } 271769ce76dSAlexander Graf 2724e767657SMax Reitz read_end: 2734e767657SMax Reitz /* curl will error out if we do not return this value */ 2744e767657SMax Reitz return size * nmemb; 275769ce76dSAlexander Graf } 276769ce76dSAlexander Graf 277769ce76dSAlexander Graf static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len, 278769ce76dSAlexander Graf CURLAIOCB *acb) 279769ce76dSAlexander Graf { 280769ce76dSAlexander Graf int i; 281769ce76dSAlexander Graf size_t end = start + len; 282*4e504535SMax Reitz size_t clamped_end = MIN(end, s->len); 283*4e504535SMax Reitz size_t clamped_len = clamped_end - start; 284769ce76dSAlexander Graf 285769ce76dSAlexander Graf for (i=0; i<CURL_NUM_STATES; i++) { 286769ce76dSAlexander Graf CURLState *state = &s->states[i]; 287769ce76dSAlexander Graf size_t buf_end = (state->buf_start + state->buf_off); 288769ce76dSAlexander Graf size_t buf_fend = (state->buf_start + state->buf_len); 289769ce76dSAlexander Graf 290769ce76dSAlexander Graf if (!state->orig_buf) 291769ce76dSAlexander Graf continue; 292769ce76dSAlexander Graf if (!state->buf_off) 293769ce76dSAlexander Graf continue; 294769ce76dSAlexander Graf 295769ce76dSAlexander Graf // Does the existing buffer cover our section? 296769ce76dSAlexander Graf if ((start >= state->buf_start) && 297769ce76dSAlexander Graf (start <= buf_end) && 298*4e504535SMax Reitz (clamped_end >= state->buf_start) && 299*4e504535SMax Reitz (clamped_end <= buf_end)) 300769ce76dSAlexander Graf { 301769ce76dSAlexander Graf char *buf = state->orig_buf + (start - state->buf_start); 302769ce76dSAlexander Graf 303*4e504535SMax Reitz qemu_iovec_from_buf(acb->qiov, 0, buf, clamped_len); 304*4e504535SMax Reitz if (clamped_len < len) { 305*4e504535SMax Reitz qemu_iovec_memset(acb->qiov, clamped_len, 0, len - clamped_len); 306*4e504535SMax Reitz } 307769ce76dSAlexander Graf acb->common.cb(acb->common.opaque, 0); 308769ce76dSAlexander Graf 309769ce76dSAlexander Graf return FIND_RET_OK; 310769ce76dSAlexander Graf } 311769ce76dSAlexander Graf 312769ce76dSAlexander Graf // Wait for unfinished chunks 313b7079df4SMatthew Booth if (state->in_use && 314b7079df4SMatthew Booth (start >= state->buf_start) && 315769ce76dSAlexander Graf (start <= buf_fend) && 316*4e504535SMax Reitz (clamped_end >= state->buf_start) && 317*4e504535SMax Reitz (clamped_end <= buf_fend)) 318769ce76dSAlexander Graf { 319769ce76dSAlexander Graf int j; 320769ce76dSAlexander Graf 321769ce76dSAlexander Graf acb->start = start - state->buf_start; 322*4e504535SMax Reitz acb->end = acb->start + clamped_len; 323769ce76dSAlexander Graf 324769ce76dSAlexander Graf for (j=0; j<CURL_NUM_ACB; j++) { 325769ce76dSAlexander Graf if (!state->acb[j]) { 326769ce76dSAlexander Graf state->acb[j] = acb; 327769ce76dSAlexander Graf return FIND_RET_WAIT; 328769ce76dSAlexander Graf } 329769ce76dSAlexander Graf } 330769ce76dSAlexander Graf } 331769ce76dSAlexander Graf } 332769ce76dSAlexander Graf 333769ce76dSAlexander Graf return FIND_RET_NONE; 334769ce76dSAlexander Graf } 335769ce76dSAlexander Graf 336838ef602SMatthew Booth static void curl_multi_check_completion(BDRVCURLState *s) 337769ce76dSAlexander Graf { 338769ce76dSAlexander Graf int msgs_in_queue; 339769ce76dSAlexander Graf 340769ce76dSAlexander Graf /* Try to find done transfers, so we can free the easy 341769ce76dSAlexander Graf * handle again. */ 3421f2cead3SMatthew Booth for (;;) { 343769ce76dSAlexander Graf CURLMsg *msg; 344769ce76dSAlexander Graf msg = curl_multi_info_read(s->multi, &msgs_in_queue); 345769ce76dSAlexander Graf 3461f2cead3SMatthew Booth /* Quit when there are no more completions */ 347769ce76dSAlexander Graf if (!msg) 348769ce76dSAlexander Graf break; 349769ce76dSAlexander Graf 3501f2cead3SMatthew Booth if (msg->msg == CURLMSG_DONE) { 351769ce76dSAlexander Graf CURLState *state = NULL; 352f6246509SMatthew Booth curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, 353f6246509SMatthew Booth (char **)&state); 354f785a5aeSNicholas Thomas 355f785a5aeSNicholas Thomas /* ACBs for successful messages get completed in curl_read_cb */ 356f785a5aeSNicholas Thomas if (msg->data.result != CURLE_OK) { 357f785a5aeSNicholas Thomas int i; 358796a060bSRichard W.M. Jones static int errcount = 100; 359796a060bSRichard W.M. Jones 360796a060bSRichard W.M. Jones /* Don't lose the original error message from curl, since 361796a060bSRichard W.M. Jones * it contains extra data. 362796a060bSRichard W.M. Jones */ 363796a060bSRichard W.M. Jones if (errcount > 0) { 364796a060bSRichard W.M. Jones error_report("curl: %s", state->errmsg); 365796a060bSRichard W.M. Jones if (--errcount == 0) { 366796a060bSRichard W.M. Jones error_report("curl: further errors suppressed"); 367796a060bSRichard W.M. Jones } 368796a060bSRichard W.M. Jones } 369796a060bSRichard W.M. Jones 370f785a5aeSNicholas Thomas for (i = 0; i < CURL_NUM_ACB; i++) { 371f785a5aeSNicholas Thomas CURLAIOCB *acb = state->acb[i]; 372f785a5aeSNicholas Thomas 373f785a5aeSNicholas Thomas if (acb == NULL) { 374f785a5aeSNicholas Thomas continue; 375f785a5aeSNicholas Thomas } 376f785a5aeSNicholas Thomas 377796a060bSRichard W.M. Jones acb->common.cb(acb->common.opaque, -EPROTO); 3788007429aSFam Zheng qemu_aio_unref(acb); 379f785a5aeSNicholas Thomas state->acb[i] = NULL; 380f785a5aeSNicholas Thomas } 381f785a5aeSNicholas Thomas } 382f785a5aeSNicholas Thomas 383769ce76dSAlexander Graf curl_clean_state(state); 384769ce76dSAlexander Graf break; 385769ce76dSAlexander Graf } 386769ce76dSAlexander Graf } 387769ce76dSAlexander Graf } 388769ce76dSAlexander Graf 389031fd1beSPeter Maydell static void curl_multi_do(void *arg) 390031fd1beSPeter Maydell { 391838ef602SMatthew Booth CURLState *s = (CURLState *)arg; 392ff5ca166SMax Reitz CURLSocket *socket, *next_socket; 393031fd1beSPeter Maydell int running; 394031fd1beSPeter Maydell int r; 395031fd1beSPeter Maydell 396838ef602SMatthew Booth if (!s->s->multi) { 397031fd1beSPeter Maydell return; 398031fd1beSPeter Maydell } 399031fd1beSPeter Maydell 400ff5ca166SMax Reitz /* Need to use _SAFE because curl_multi_socket_action() may trigger 401ff5ca166SMax Reitz * curl_sock_cb() which might modify this list */ 402ff5ca166SMax Reitz QLIST_FOREACH_SAFE(socket, &s->sockets, next, next_socket) { 403031fd1beSPeter Maydell do { 404ff5ca166SMax Reitz r = curl_multi_socket_action(s->s->multi, socket->fd, 0, &running); 405031fd1beSPeter Maydell } while (r == CURLM_CALL_MULTI_PERFORM); 406ff5ca166SMax Reitz } 407838ef602SMatthew Booth } 408838ef602SMatthew Booth 409838ef602SMatthew Booth static void curl_multi_read(void *arg) 410838ef602SMatthew Booth { 411838ef602SMatthew Booth CURLState *s = (CURLState *)arg; 412838ef602SMatthew Booth 413838ef602SMatthew Booth curl_multi_do(arg); 414838ef602SMatthew Booth curl_multi_check_completion(s->s); 415031fd1beSPeter Maydell } 416031fd1beSPeter Maydell 417031fd1beSPeter Maydell static void curl_multi_timeout_do(void *arg) 418031fd1beSPeter Maydell { 419031fd1beSPeter Maydell #ifdef NEED_CURL_TIMER_CALLBACK 420031fd1beSPeter Maydell BDRVCURLState *s = (BDRVCURLState *)arg; 421031fd1beSPeter Maydell int running; 422031fd1beSPeter Maydell 423031fd1beSPeter Maydell if (!s->multi) { 424031fd1beSPeter Maydell return; 425031fd1beSPeter Maydell } 426031fd1beSPeter Maydell 427031fd1beSPeter Maydell curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running); 428031fd1beSPeter Maydell 429838ef602SMatthew Booth curl_multi_check_completion(s); 430031fd1beSPeter Maydell #else 431031fd1beSPeter Maydell abort(); 432031fd1beSPeter Maydell #endif 433031fd1beSPeter Maydell } 434031fd1beSPeter Maydell 435a2f468e4SRichard W.M. Jones static CURLState *curl_init_state(BlockDriverState *bs, BDRVCURLState *s) 436769ce76dSAlexander Graf { 437769ce76dSAlexander Graf CURLState *state = NULL; 438769ce76dSAlexander Graf int i, j; 439769ce76dSAlexander Graf 440769ce76dSAlexander Graf do { 441769ce76dSAlexander Graf for (i=0; i<CURL_NUM_STATES; i++) { 442769ce76dSAlexander Graf for (j=0; j<CURL_NUM_ACB; j++) 443769ce76dSAlexander Graf if (s->states[i].acb[j]) 444769ce76dSAlexander Graf continue; 445769ce76dSAlexander Graf if (s->states[i].in_use) 446769ce76dSAlexander Graf continue; 447769ce76dSAlexander Graf 448769ce76dSAlexander Graf state = &s->states[i]; 449769ce76dSAlexander Graf state->in_use = 1; 450769ce76dSAlexander Graf break; 451769ce76dSAlexander Graf } 452769ce76dSAlexander Graf if (!state) { 453a2f468e4SRichard W.M. Jones aio_poll(bdrv_get_aio_context(bs), true); 454769ce76dSAlexander Graf } 455769ce76dSAlexander Graf } while(!state); 456769ce76dSAlexander Graf 4579e550b32SMatthew Booth if (!state->curl) { 458769ce76dSAlexander Graf state->curl = curl_easy_init(); 4599e550b32SMatthew Booth if (!state->curl) { 460769ce76dSAlexander Graf return NULL; 4619e550b32SMatthew Booth } 462769ce76dSAlexander Graf curl_easy_setopt(state->curl, CURLOPT_URL, s->url); 46397a3ea57SMatthew Booth curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER, 46497a3ea57SMatthew Booth (long) s->sslverify); 465a94f83d9SRichard W.M. Jones if (s->cookie) { 466a94f83d9SRichard W.M. Jones curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie); 467a94f83d9SRichard W.M. Jones } 468f76faedaSRichard W.M. Jones curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, (long)s->timeout); 4699e550b32SMatthew Booth curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, 4709e550b32SMatthew Booth (void *)curl_read_cb); 471769ce76dSAlexander Graf curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state); 472769ce76dSAlexander Graf curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state); 473769ce76dSAlexander Graf curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1); 474769ce76dSAlexander Graf curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1); 475769ce76dSAlexander Graf curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1); 476769ce76dSAlexander Graf curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg); 477f785a5aeSNicholas Thomas curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1); 478769ce76dSAlexander Graf 4791bff9606SDaniel P. Berrange if (s->username) { 4801bff9606SDaniel P. Berrange curl_easy_setopt(state->curl, CURLOPT_USERNAME, s->username); 4811bff9606SDaniel P. Berrange } 4821bff9606SDaniel P. Berrange if (s->password) { 4831bff9606SDaniel P. Berrange curl_easy_setopt(state->curl, CURLOPT_PASSWORD, s->password); 4841bff9606SDaniel P. Berrange } 4851bff9606SDaniel P. Berrange if (s->proxyusername) { 4861bff9606SDaniel P. Berrange curl_easy_setopt(state->curl, 4871bff9606SDaniel P. Berrange CURLOPT_PROXYUSERNAME, s->proxyusername); 4881bff9606SDaniel P. Berrange } 4891bff9606SDaniel P. Berrange if (s->proxypassword) { 4901bff9606SDaniel P. Berrange curl_easy_setopt(state->curl, 4911bff9606SDaniel P. Berrange CURLOPT_PROXYPASSWORD, s->proxypassword); 4921bff9606SDaniel P. Berrange } 4931bff9606SDaniel P. Berrange 494fb6d1bbdSStefan Hajnoczi /* Restrict supported protocols to avoid security issues in the more 495fb6d1bbdSStefan Hajnoczi * obscure protocols. For example, do not allow POP3/SMTP/IMAP see 496fb6d1bbdSStefan Hajnoczi * CVE-2013-0249. 4978a8f5840SStefan Hajnoczi * 4988a8f5840SStefan Hajnoczi * Restricting protocols is only supported from 7.19.4 upwards. 499fb6d1bbdSStefan Hajnoczi */ 5008a8f5840SStefan Hajnoczi #if LIBCURL_VERSION_NUM >= 0x071304 501fb6d1bbdSStefan Hajnoczi curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS); 502fb6d1bbdSStefan Hajnoczi curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS); 5038a8f5840SStefan Hajnoczi #endif 504fb6d1bbdSStefan Hajnoczi 505769ce76dSAlexander Graf #ifdef DEBUG_VERBOSE 506769ce76dSAlexander Graf curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1); 507769ce76dSAlexander Graf #endif 5089e550b32SMatthew Booth } 509769ce76dSAlexander Graf 510ff5ca166SMax Reitz QLIST_INIT(&state->sockets); 511769ce76dSAlexander Graf state->s = s; 512769ce76dSAlexander Graf 513769ce76dSAlexander Graf return state; 514769ce76dSAlexander Graf } 515769ce76dSAlexander Graf 516769ce76dSAlexander Graf static void curl_clean_state(CURLState *s) 517769ce76dSAlexander Graf { 518769ce76dSAlexander Graf if (s->s->multi) 519769ce76dSAlexander Graf curl_multi_remove_handle(s->s->multi, s->curl); 520ff5ca166SMax Reitz 521ff5ca166SMax Reitz while (!QLIST_EMPTY(&s->sockets)) { 522ff5ca166SMax Reitz CURLSocket *socket = QLIST_FIRST(&s->sockets); 523ff5ca166SMax Reitz 524ff5ca166SMax Reitz QLIST_REMOVE(socket, next); 525ff5ca166SMax Reitz g_free(socket); 526ff5ca166SMax Reitz } 527ff5ca166SMax Reitz 528769ce76dSAlexander Graf s->in_use = 0; 529769ce76dSAlexander Graf } 530769ce76dSAlexander Graf 5318e6d58cdSKevin Wolf static void curl_parse_filename(const char *filename, QDict *options, 5328e6d58cdSKevin Wolf Error **errp) 533769ce76dSAlexander Graf { 534e3542c67SMatthew Booth qdict_put(options, CURL_BLOCK_OPT_URL, qstring_from_str(filename)); 5358e6d58cdSKevin Wolf } 5368e6d58cdSKevin Wolf 53763f0f45fSStefan Hajnoczi static void curl_detach_aio_context(BlockDriverState *bs) 53863f0f45fSStefan Hajnoczi { 53963f0f45fSStefan Hajnoczi BDRVCURLState *s = bs->opaque; 54063f0f45fSStefan Hajnoczi int i; 54163f0f45fSStefan Hajnoczi 54263f0f45fSStefan Hajnoczi for (i = 0; i < CURL_NUM_STATES; i++) { 54363f0f45fSStefan Hajnoczi if (s->states[i].in_use) { 54463f0f45fSStefan Hajnoczi curl_clean_state(&s->states[i]); 54563f0f45fSStefan Hajnoczi } 54663f0f45fSStefan Hajnoczi if (s->states[i].curl) { 54763f0f45fSStefan Hajnoczi curl_easy_cleanup(s->states[i].curl); 54863f0f45fSStefan Hajnoczi s->states[i].curl = NULL; 54963f0f45fSStefan Hajnoczi } 55063f0f45fSStefan Hajnoczi g_free(s->states[i].orig_buf); 55163f0f45fSStefan Hajnoczi s->states[i].orig_buf = NULL; 55263f0f45fSStefan Hajnoczi } 55363f0f45fSStefan Hajnoczi if (s->multi) { 55463f0f45fSStefan Hajnoczi curl_multi_cleanup(s->multi); 55563f0f45fSStefan Hajnoczi s->multi = NULL; 55663f0f45fSStefan Hajnoczi } 55763f0f45fSStefan Hajnoczi 55863f0f45fSStefan Hajnoczi timer_del(&s->timer); 55963f0f45fSStefan Hajnoczi } 56063f0f45fSStefan Hajnoczi 56163f0f45fSStefan Hajnoczi static void curl_attach_aio_context(BlockDriverState *bs, 56263f0f45fSStefan Hajnoczi AioContext *new_context) 56363f0f45fSStefan Hajnoczi { 56463f0f45fSStefan Hajnoczi BDRVCURLState *s = bs->opaque; 56563f0f45fSStefan Hajnoczi 56663f0f45fSStefan Hajnoczi aio_timer_init(new_context, &s->timer, 56763f0f45fSStefan Hajnoczi QEMU_CLOCK_REALTIME, SCALE_NS, 56863f0f45fSStefan Hajnoczi curl_multi_timeout_do, s); 56963f0f45fSStefan Hajnoczi 57063f0f45fSStefan Hajnoczi assert(!s->multi); 57163f0f45fSStefan Hajnoczi s->multi = curl_multi_init(); 57263f0f45fSStefan Hajnoczi s->aio_context = new_context; 57363f0f45fSStefan Hajnoczi curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb); 57463f0f45fSStefan Hajnoczi #ifdef NEED_CURL_TIMER_CALLBACK 57563f0f45fSStefan Hajnoczi curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s); 57663f0f45fSStefan Hajnoczi curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb); 57763f0f45fSStefan Hajnoczi #endif 57863f0f45fSStefan Hajnoczi } 57963f0f45fSStefan Hajnoczi 5808e6d58cdSKevin Wolf static QemuOptsList runtime_opts = { 5818e6d58cdSKevin Wolf .name = "curl", 5828e6d58cdSKevin Wolf .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head), 5838e6d58cdSKevin Wolf .desc = { 5848e6d58cdSKevin Wolf { 585e3542c67SMatthew Booth .name = CURL_BLOCK_OPT_URL, 5868e6d58cdSKevin Wolf .type = QEMU_OPT_STRING, 5878e6d58cdSKevin Wolf .help = "URL to open", 5888e6d58cdSKevin Wolf }, 5898e6d58cdSKevin Wolf { 590e3542c67SMatthew Booth .name = CURL_BLOCK_OPT_READAHEAD, 5918e6d58cdSKevin Wolf .type = QEMU_OPT_SIZE, 5928e6d58cdSKevin Wolf .help = "Readahead size", 5938e6d58cdSKevin Wolf }, 59497a3ea57SMatthew Booth { 59597a3ea57SMatthew Booth .name = CURL_BLOCK_OPT_SSLVERIFY, 59697a3ea57SMatthew Booth .type = QEMU_OPT_BOOL, 59797a3ea57SMatthew Booth .help = "Verify SSL certificate" 59897a3ea57SMatthew Booth }, 599212aefaaSDaniel Henrique Barboza { 600212aefaaSDaniel Henrique Barboza .name = CURL_BLOCK_OPT_TIMEOUT, 601212aefaaSDaniel Henrique Barboza .type = QEMU_OPT_NUMBER, 602212aefaaSDaniel Henrique Barboza .help = "Curl timeout" 603212aefaaSDaniel Henrique Barboza }, 604a94f83d9SRichard W.M. Jones { 605a94f83d9SRichard W.M. Jones .name = CURL_BLOCK_OPT_COOKIE, 606a94f83d9SRichard W.M. Jones .type = QEMU_OPT_STRING, 607a94f83d9SRichard W.M. Jones .help = "Pass the cookie or list of cookies with each request" 608a94f83d9SRichard W.M. Jones }, 6091bff9606SDaniel P. Berrange { 6101bff9606SDaniel P. Berrange .name = CURL_BLOCK_OPT_USERNAME, 6111bff9606SDaniel P. Berrange .type = QEMU_OPT_STRING, 6121bff9606SDaniel P. Berrange .help = "Username for HTTP auth" 6131bff9606SDaniel P. Berrange }, 6141bff9606SDaniel P. Berrange { 6151bff9606SDaniel P. Berrange .name = CURL_BLOCK_OPT_PASSWORD_SECRET, 6161bff9606SDaniel P. Berrange .type = QEMU_OPT_STRING, 6171bff9606SDaniel P. Berrange .help = "ID of secret used as password for HTTP auth", 6181bff9606SDaniel P. Berrange }, 6191bff9606SDaniel P. Berrange { 6201bff9606SDaniel P. Berrange .name = CURL_BLOCK_OPT_PROXY_USERNAME, 6211bff9606SDaniel P. Berrange .type = QEMU_OPT_STRING, 6221bff9606SDaniel P. Berrange .help = "Username for HTTP proxy auth" 6231bff9606SDaniel P. Berrange }, 6241bff9606SDaniel P. Berrange { 6251bff9606SDaniel P. Berrange .name = CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET, 6261bff9606SDaniel P. Berrange .type = QEMU_OPT_STRING, 6271bff9606SDaniel P. Berrange .help = "ID of secret used as password for HTTP proxy auth", 6281bff9606SDaniel P. Berrange }, 6298e6d58cdSKevin Wolf { /* end of list */ } 6308e6d58cdSKevin Wolf }, 6318e6d58cdSKevin Wolf }; 6328e6d58cdSKevin Wolf 6331bff9606SDaniel P. Berrange 634015a1036SMax Reitz static int curl_open(BlockDriverState *bs, QDict *options, int flags, 635015a1036SMax Reitz Error **errp) 6368e6d58cdSKevin Wolf { 6378e6d58cdSKevin Wolf BDRVCURLState *s = bs->opaque; 6388e6d58cdSKevin Wolf CURLState *state = NULL; 6398e6d58cdSKevin Wolf QemuOpts *opts; 6408e6d58cdSKevin Wolf Error *local_err = NULL; 6418e6d58cdSKevin Wolf const char *file; 642a94f83d9SRichard W.M. Jones const char *cookie; 6438e6d58cdSKevin Wolf double d; 6441bff9606SDaniel P. Berrange const char *secretid; 6458e6d58cdSKevin Wolf 6468e6d58cdSKevin Wolf static int inited = 0; 6478e6d58cdSKevin Wolf 648a7cea2baSRichard W.M. Jones if (flags & BDRV_O_RDWR) { 6492a94fee3SPaolo Bonzini error_setg(errp, "curl block device does not support writes"); 650a7cea2baSRichard W.M. Jones return -EROFS; 651a7cea2baSRichard W.M. Jones } 652a7cea2baSRichard W.M. Jones 65387ea75d5SPeter Crosthwaite opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort); 6548e6d58cdSKevin Wolf qemu_opts_absorb_qdict(opts, options, &local_err); 65584d18f06SMarkus Armbruster if (local_err) { 6562a94fee3SPaolo Bonzini error_propagate(errp, local_err); 6578e6d58cdSKevin Wolf goto out_noclean; 6588e6d58cdSKevin Wolf } 6598e6d58cdSKevin Wolf 660e3542c67SMatthew Booth s->readahead_size = qemu_opt_get_size(opts, CURL_BLOCK_OPT_READAHEAD, 661e3542c67SMatthew Booth READ_AHEAD_DEFAULT); 662c76f4952SNolan if ((s->readahead_size & 0x1ff) != 0) { 6632a94fee3SPaolo Bonzini error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512", 664c76f4952SNolan s->readahead_size); 665c76f4952SNolan goto out_noclean; 666c76f4952SNolan } 667c76f4952SNolan 668212aefaaSDaniel Henrique Barboza s->timeout = qemu_opt_get_number(opts, CURL_BLOCK_OPT_TIMEOUT, 669212aefaaSDaniel Henrique Barboza CURL_TIMEOUT_DEFAULT); 670f76faedaSRichard W.M. Jones if (s->timeout > CURL_TIMEOUT_MAX) { 671f76faedaSRichard W.M. Jones error_setg(errp, "timeout parameter is too large or negative"); 672f76faedaSRichard W.M. Jones goto out_noclean; 673f76faedaSRichard W.M. Jones } 674212aefaaSDaniel Henrique Barboza 67597a3ea57SMatthew Booth s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true); 67697a3ea57SMatthew Booth 677a94f83d9SRichard W.M. Jones cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE); 678a94f83d9SRichard W.M. Jones s->cookie = g_strdup(cookie); 679a94f83d9SRichard W.M. Jones 680e3542c67SMatthew Booth file = qemu_opt_get(opts, CURL_BLOCK_OPT_URL); 6818e6d58cdSKevin Wolf if (file == NULL) { 6822a94fee3SPaolo Bonzini error_setg(errp, "curl block driver requires an 'url' option"); 6838e6d58cdSKevin Wolf goto out_noclean; 6848e6d58cdSKevin Wolf } 6858e6d58cdSKevin Wolf 6861bff9606SDaniel P. Berrange s->username = g_strdup(qemu_opt_get(opts, CURL_BLOCK_OPT_USERNAME)); 6871bff9606SDaniel P. Berrange secretid = qemu_opt_get(opts, CURL_BLOCK_OPT_PASSWORD_SECRET); 6881bff9606SDaniel P. Berrange 6891bff9606SDaniel P. Berrange if (secretid) { 6901bff9606SDaniel P. Berrange s->password = qcrypto_secret_lookup_as_utf8(secretid, errp); 6911bff9606SDaniel P. Berrange if (!s->password) { 6921bff9606SDaniel P. Berrange goto out_noclean; 6931bff9606SDaniel P. Berrange } 6941bff9606SDaniel P. Berrange } 6951bff9606SDaniel P. Berrange 6961bff9606SDaniel P. Berrange s->proxyusername = g_strdup( 6971bff9606SDaniel P. Berrange qemu_opt_get(opts, CURL_BLOCK_OPT_PROXY_USERNAME)); 6981bff9606SDaniel P. Berrange secretid = qemu_opt_get(opts, CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET); 6991bff9606SDaniel P. Berrange if (secretid) { 7001bff9606SDaniel P. Berrange s->proxypassword = qcrypto_secret_lookup_as_utf8(secretid, errp); 7011bff9606SDaniel P. Berrange if (!s->proxypassword) { 7021bff9606SDaniel P. Berrange goto out_noclean; 7031bff9606SDaniel P. Berrange } 7041bff9606SDaniel P. Berrange } 7051bff9606SDaniel P. Berrange 706769ce76dSAlexander Graf if (!inited) { 707769ce76dSAlexander Graf curl_global_init(CURL_GLOBAL_ALL); 708769ce76dSAlexander Graf inited = 1; 709769ce76dSAlexander Graf } 710769ce76dSAlexander Graf 711d0f2c4c6Smalc DPRINTF("CURL: Opening %s\n", file); 71263f0f45fSStefan Hajnoczi s->aio_context = bdrv_get_aio_context(bs); 7138e6d58cdSKevin Wolf s->url = g_strdup(file); 714a2f468e4SRichard W.M. Jones state = curl_init_state(bs, s); 715769ce76dSAlexander Graf if (!state) 716769ce76dSAlexander Graf goto out_noclean; 717769ce76dSAlexander Graf 718769ce76dSAlexander Graf // Get file size 719769ce76dSAlexander Graf 7203494d650SFam Zheng s->accept_range = false; 721769ce76dSAlexander Graf curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1); 7223494d650SFam Zheng curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION, 7233494d650SFam Zheng curl_header_cb); 7243494d650SFam Zheng curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s); 725769ce76dSAlexander Graf if (curl_easy_perform(state->curl)) 726769ce76dSAlexander Graf goto out; 727a41c4578STomáš Golembiovský if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d)) { 728769ce76dSAlexander Graf goto out; 729a41c4578STomáš Golembiovský } 730a41c4578STomáš Golembiovský /* Prior CURL 7.19.4 return value of 0 could mean that the file size is not 731a41c4578STomáš Golembiovský * know or the size is zero. From 7.19.4 CURL returns -1 if size is not 732a41c4578STomáš Golembiovský * known and zero if it is realy zero-length file. */ 733a41c4578STomáš Golembiovský #if LIBCURL_VERSION_NUM >= 0x071304 734a41c4578STomáš Golembiovský if (d < 0) { 735a41c4578STomáš Golembiovský pstrcpy(state->errmsg, CURL_ERROR_SIZE, 736a41c4578STomáš Golembiovský "Server didn't report file size."); 737a41c4578STomáš Golembiovský goto out; 738a41c4578STomáš Golembiovský } 739a41c4578STomáš Golembiovský #else 740a41c4578STomáš Golembiovský if (d <= 0) { 741a41c4578STomáš Golembiovský pstrcpy(state->errmsg, CURL_ERROR_SIZE, 742a41c4578STomáš Golembiovský "Unknown file size or zero-length file."); 743a41c4578STomáš Golembiovský goto out; 744a41c4578STomáš Golembiovský } 745a41c4578STomáš Golembiovský #endif 746a41c4578STomáš Golembiovský 747a41c4578STomáš Golembiovský s->len = (size_t)d; 748a41c4578STomáš Golembiovský 7493494d650SFam Zheng if ((!strncasecmp(s->url, "http://", strlen("http://")) 7503494d650SFam Zheng || !strncasecmp(s->url, "https://", strlen("https://"))) 7513494d650SFam Zheng && !s->accept_range) { 7523494d650SFam Zheng pstrcpy(state->errmsg, CURL_ERROR_SIZE, 7533494d650SFam Zheng "Server does not support 'range' (byte ranges)."); 7543494d650SFam Zheng goto out; 7553494d650SFam Zheng } 7560bfcd599SBlue Swirl DPRINTF("CURL: Size = %zd\n", s->len); 757769ce76dSAlexander Graf 758769ce76dSAlexander Graf curl_clean_state(state); 759769ce76dSAlexander Graf curl_easy_cleanup(state->curl); 760769ce76dSAlexander Graf state->curl = NULL; 761769ce76dSAlexander Graf 76263f0f45fSStefan Hajnoczi curl_attach_aio_context(bs, bdrv_get_aio_context(bs)); 763769ce76dSAlexander Graf 7648e6d58cdSKevin Wolf qemu_opts_del(opts); 765769ce76dSAlexander Graf return 0; 766769ce76dSAlexander Graf 767769ce76dSAlexander Graf out: 768acd7fdc6SMaria Kustova error_setg(errp, "CURL: Error opening file: %s", state->errmsg); 769769ce76dSAlexander Graf curl_easy_cleanup(state->curl); 770769ce76dSAlexander Graf state->curl = NULL; 771769ce76dSAlexander Graf out_noclean: 772a94f83d9SRichard W.M. Jones g_free(s->cookie); 7738e6d58cdSKevin Wolf g_free(s->url); 7748e6d58cdSKevin Wolf qemu_opts_del(opts); 775769ce76dSAlexander Graf return -EINVAL; 776769ce76dSAlexander Graf } 777769ce76dSAlexander Graf 778d7331bedSStefan Hajnoczi static const AIOCBInfo curl_aiocb_info = { 779c16b5a2cSChristoph Hellwig .aiocb_size = sizeof(CURLAIOCB), 780c16b5a2cSChristoph Hellwig }; 781c16b5a2cSChristoph Hellwig 782363c3c85SNick Thomas 783363c3c85SNick Thomas static void curl_readv_bh_cb(void *p) 784769ce76dSAlexander Graf { 785769ce76dSAlexander Graf CURLState *state; 786b69cdef8SMatthew Booth int running; 787769ce76dSAlexander Graf 788363c3c85SNick Thomas CURLAIOCB *acb = p; 789363c3c85SNick Thomas BDRVCURLState *s = acb->common.bs->opaque; 790769ce76dSAlexander Graf 7919054d9f6SMax Reitz size_t start = acb->sector_num * BDRV_SECTOR_SIZE; 792363c3c85SNick Thomas size_t end; 793769ce76dSAlexander Graf 794769ce76dSAlexander Graf // In case we have the requested data already (e.g. read-ahead), 795769ce76dSAlexander Graf // we can just call the callback and be done. 7969054d9f6SMax Reitz switch (curl_find_buf(s, start, acb->nb_sectors * BDRV_SECTOR_SIZE, acb)) { 797769ce76dSAlexander Graf case FIND_RET_OK: 7988007429aSFam Zheng qemu_aio_unref(acb); 799769ce76dSAlexander Graf // fall through 800769ce76dSAlexander Graf case FIND_RET_WAIT: 801363c3c85SNick Thomas return; 802769ce76dSAlexander Graf default: 803769ce76dSAlexander Graf break; 804769ce76dSAlexander Graf } 805769ce76dSAlexander Graf 806769ce76dSAlexander Graf // No cache found, so let's start a new request 807a2f468e4SRichard W.M. Jones state = curl_init_state(acb->common.bs, s); 808363c3c85SNick Thomas if (!state) { 809363c3c85SNick Thomas acb->common.cb(acb->common.opaque, -EIO); 8108007429aSFam Zheng qemu_aio_unref(acb); 811363c3c85SNick Thomas return; 812363c3c85SNick Thomas } 813769ce76dSAlexander Graf 814769ce76dSAlexander Graf acb->start = 0; 815*4e504535SMax Reitz acb->end = MIN(acb->nb_sectors * BDRV_SECTOR_SIZE, s->len - start); 816769ce76dSAlexander Graf 817769ce76dSAlexander Graf state->buf_off = 0; 8187267c094SAnthony Liguori g_free(state->orig_buf); 819769ce76dSAlexander Graf state->buf_start = start; 820*4e504535SMax Reitz state->buf_len = MIN(acb->end + s->readahead_size, s->len - start); 821*4e504535SMax Reitz end = start + state->buf_len - 1; 8228dc7a772SKevin Wolf state->orig_buf = g_try_malloc(state->buf_len); 8238dc7a772SKevin Wolf if (state->buf_len && state->orig_buf == NULL) { 8248dc7a772SKevin Wolf curl_clean_state(state); 8258dc7a772SKevin Wolf acb->common.cb(acb->common.opaque, -ENOMEM); 8268007429aSFam Zheng qemu_aio_unref(acb); 8278dc7a772SKevin Wolf return; 8288dc7a772SKevin Wolf } 829769ce76dSAlexander Graf state->acb[0] = acb; 830769ce76dSAlexander Graf 8310bfcd599SBlue Swirl snprintf(state->range, 127, "%zd-%zd", start, end); 8329054d9f6SMax Reitz DPRINTF("CURL (AIO): Reading %llu at %zd (%s)\n", 8339054d9f6SMax Reitz (acb->nb_sectors * BDRV_SECTOR_SIZE), start, state->range); 834769ce76dSAlexander Graf curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); 835769ce76dSAlexander Graf 836769ce76dSAlexander Graf curl_multi_add_handle(s->multi, state->curl); 837769ce76dSAlexander Graf 838b69cdef8SMatthew Booth /* Tell curl it needs to kick things off */ 839b69cdef8SMatthew Booth curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running); 840363c3c85SNick Thomas } 841363c3c85SNick Thomas 8427c84b1b8SMarkus Armbruster static BlockAIOCB *curl_aio_readv(BlockDriverState *bs, 843363c3c85SNick Thomas int64_t sector_num, QEMUIOVector *qiov, int nb_sectors, 844097310b5SMarkus Armbruster BlockCompletionFunc *cb, void *opaque) 845363c3c85SNick Thomas { 846363c3c85SNick Thomas CURLAIOCB *acb; 847363c3c85SNick Thomas 848d7331bedSStefan Hajnoczi acb = qemu_aio_get(&curl_aiocb_info, bs, cb, opaque); 849363c3c85SNick Thomas 850363c3c85SNick Thomas acb->qiov = qiov; 851363c3c85SNick Thomas acb->sector_num = sector_num; 852363c3c85SNick Thomas acb->nb_sectors = nb_sectors; 853363c3c85SNick Thomas 854fffb6e12SPaolo Bonzini aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), curl_readv_bh_cb, acb); 855769ce76dSAlexander Graf return &acb->common; 856769ce76dSAlexander Graf } 857769ce76dSAlexander Graf 858769ce76dSAlexander Graf static void curl_close(BlockDriverState *bs) 859769ce76dSAlexander Graf { 860769ce76dSAlexander Graf BDRVCURLState *s = bs->opaque; 861769ce76dSAlexander Graf 862d0f2c4c6Smalc DPRINTF("CURL: Close\n"); 86363f0f45fSStefan Hajnoczi curl_detach_aio_context(bs); 864031fd1beSPeter Maydell 865a94f83d9SRichard W.M. Jones g_free(s->cookie); 86645724d6dSStefan Weil g_free(s->url); 867769ce76dSAlexander Graf } 868769ce76dSAlexander Graf 869769ce76dSAlexander Graf static int64_t curl_getlength(BlockDriverState *bs) 870769ce76dSAlexander Graf { 871769ce76dSAlexander Graf BDRVCURLState *s = bs->opaque; 872769ce76dSAlexander Graf return s->len; 873769ce76dSAlexander Graf } 874769ce76dSAlexander Graf 875769ce76dSAlexander Graf static BlockDriver bdrv_http = { 876769ce76dSAlexander Graf .format_name = "http", 877769ce76dSAlexander Graf .protocol_name = "http", 878769ce76dSAlexander Graf 879769ce76dSAlexander Graf .instance_size = sizeof(BDRVCURLState), 8808e6d58cdSKevin Wolf .bdrv_parse_filename = curl_parse_filename, 88166f82ceeSKevin Wolf .bdrv_file_open = curl_open, 882769ce76dSAlexander Graf .bdrv_close = curl_close, 883769ce76dSAlexander Graf .bdrv_getlength = curl_getlength, 884769ce76dSAlexander Graf 885769ce76dSAlexander Graf .bdrv_aio_readv = curl_aio_readv, 88663f0f45fSStefan Hajnoczi 88763f0f45fSStefan Hajnoczi .bdrv_detach_aio_context = curl_detach_aio_context, 88863f0f45fSStefan Hajnoczi .bdrv_attach_aio_context = curl_attach_aio_context, 889769ce76dSAlexander Graf }; 890769ce76dSAlexander Graf 891769ce76dSAlexander Graf static BlockDriver bdrv_https = { 892769ce76dSAlexander Graf .format_name = "https", 893769ce76dSAlexander Graf .protocol_name = "https", 894769ce76dSAlexander Graf 895769ce76dSAlexander Graf .instance_size = sizeof(BDRVCURLState), 8968e6d58cdSKevin Wolf .bdrv_parse_filename = curl_parse_filename, 89766f82ceeSKevin Wolf .bdrv_file_open = curl_open, 898769ce76dSAlexander Graf .bdrv_close = curl_close, 899769ce76dSAlexander Graf .bdrv_getlength = curl_getlength, 900769ce76dSAlexander Graf 901769ce76dSAlexander Graf .bdrv_aio_readv = curl_aio_readv, 90263f0f45fSStefan Hajnoczi 90363f0f45fSStefan Hajnoczi .bdrv_detach_aio_context = curl_detach_aio_context, 90463f0f45fSStefan Hajnoczi .bdrv_attach_aio_context = curl_attach_aio_context, 905769ce76dSAlexander Graf }; 906769ce76dSAlexander Graf 907769ce76dSAlexander Graf static BlockDriver bdrv_ftp = { 908769ce76dSAlexander Graf .format_name = "ftp", 909769ce76dSAlexander Graf .protocol_name = "ftp", 910769ce76dSAlexander Graf 911769ce76dSAlexander Graf .instance_size = sizeof(BDRVCURLState), 9128e6d58cdSKevin Wolf .bdrv_parse_filename = curl_parse_filename, 91366f82ceeSKevin Wolf .bdrv_file_open = curl_open, 914769ce76dSAlexander Graf .bdrv_close = curl_close, 915769ce76dSAlexander Graf .bdrv_getlength = curl_getlength, 916769ce76dSAlexander Graf 917769ce76dSAlexander Graf .bdrv_aio_readv = curl_aio_readv, 91863f0f45fSStefan Hajnoczi 91963f0f45fSStefan Hajnoczi .bdrv_detach_aio_context = curl_detach_aio_context, 92063f0f45fSStefan Hajnoczi .bdrv_attach_aio_context = curl_attach_aio_context, 921769ce76dSAlexander Graf }; 922769ce76dSAlexander Graf 923769ce76dSAlexander Graf static BlockDriver bdrv_ftps = { 924769ce76dSAlexander Graf .format_name = "ftps", 925769ce76dSAlexander Graf .protocol_name = "ftps", 926769ce76dSAlexander Graf 927769ce76dSAlexander Graf .instance_size = sizeof(BDRVCURLState), 9288e6d58cdSKevin Wolf .bdrv_parse_filename = curl_parse_filename, 92966f82ceeSKevin Wolf .bdrv_file_open = curl_open, 930769ce76dSAlexander Graf .bdrv_close = curl_close, 931769ce76dSAlexander Graf .bdrv_getlength = curl_getlength, 932769ce76dSAlexander Graf 933769ce76dSAlexander Graf .bdrv_aio_readv = curl_aio_readv, 93463f0f45fSStefan Hajnoczi 93563f0f45fSStefan Hajnoczi .bdrv_detach_aio_context = curl_detach_aio_context, 93663f0f45fSStefan Hajnoczi .bdrv_attach_aio_context = curl_attach_aio_context, 937769ce76dSAlexander Graf }; 938769ce76dSAlexander Graf 939769ce76dSAlexander Graf static void curl_block_init(void) 940769ce76dSAlexander Graf { 941769ce76dSAlexander Graf bdrv_register(&bdrv_http); 942769ce76dSAlexander Graf bdrv_register(&bdrv_https); 943769ce76dSAlexander Graf bdrv_register(&bdrv_ftp); 944769ce76dSAlexander Graf bdrv_register(&bdrv_ftps); 945769ce76dSAlexander Graf } 946769ce76dSAlexander Graf 947769ce76dSAlexander Graf block_init(curl_block_init); 948