qemu/block/curl.c
<<
>>
Prefs
   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
  25#include "qemu/osdep.h"
  26#include "qapi/error.h"
  27#include "qemu/error-report.h"
  28#include "qemu/module.h"
  29#include "qemu/option.h"
  30#include "block/block-io.h"
  31#include "block/block_int.h"
  32#include "qapi/qmp/qdict.h"
  33#include "qapi/qmp/qstring.h"
  34#include "crypto/secret.h"
  35#include <curl/curl.h>
  36#include "qemu/cutils.h"
  37#include "trace.h"
  38
  39// #define DEBUG_VERBOSE
  40
  41/* CURL 7.85.0 switches to a string based API for specifying
  42 * the desired protocols.
  43 */
  44#if LIBCURL_VERSION_NUM >= 0x075500
  45#define PROTOCOLS "HTTP,HTTPS,FTP,FTPS"
  46#else
  47#define PROTOCOLS (CURLPROTO_HTTP | CURLPROTO_HTTPS | \
  48                   CURLPROTO_FTP | CURLPROTO_FTPS)
  49#endif
  50
  51#define CURL_NUM_STATES 8
  52#define CURL_NUM_ACB    8
  53#define CURL_TIMEOUT_MAX 10000
  54
  55#define CURL_BLOCK_OPT_URL       "url"
  56#define CURL_BLOCK_OPT_READAHEAD "readahead"
  57#define CURL_BLOCK_OPT_SSLVERIFY "sslverify"
  58#define CURL_BLOCK_OPT_TIMEOUT "timeout"
  59#define CURL_BLOCK_OPT_COOKIE    "cookie"
  60#define CURL_BLOCK_OPT_COOKIE_SECRET "cookie-secret"
  61#define CURL_BLOCK_OPT_USERNAME "username"
  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"
  65
  66#define CURL_BLOCK_OPT_READAHEAD_DEFAULT (256 * 1024)
  67#define CURL_BLOCK_OPT_SSLVERIFY_DEFAULT true
  68#define CURL_BLOCK_OPT_TIMEOUT_DEFAULT 5
  69
  70struct BDRVCURLState;
  71struct CURLState;
  72
  73static bool libcurl_initialized;
  74
  75typedef struct CURLAIOCB {
  76    Coroutine *co;
  77    QEMUIOVector *qiov;
  78
  79    uint64_t offset;
  80    uint64_t bytes;
  81    int ret;
  82
  83    size_t start;
  84    size_t end;
  85} CURLAIOCB;
  86
  87typedef struct CURLSocket {
  88    int fd;
  89    struct BDRVCURLState *s;
  90} CURLSocket;
  91
  92typedef struct CURLState
  93{
  94    struct BDRVCURLState *s;
  95    CURLAIOCB *acb[CURL_NUM_ACB];
  96    CURL *curl;
  97    char *orig_buf;
  98    uint64_t buf_start;
  99    size_t buf_off;
 100    size_t buf_len;
 101    char range[128];
 102    char errmsg[CURL_ERROR_SIZE];
 103    char in_use;
 104} CURLState;
 105
 106typedef struct BDRVCURLState {
 107    CURLM *multi;
 108    QEMUTimer timer;
 109    uint64_t len;
 110    CURLState states[CURL_NUM_STATES];
 111    GHashTable *sockets; /* GINT_TO_POINTER(fd) -> socket */
 112    char *url;
 113    size_t readahead_size;
 114    bool sslverify;
 115    uint64_t timeout;
 116    char *cookie;
 117    bool accept_range;
 118    AioContext *aio_context;
 119    QemuMutex mutex;
 120    CoQueue free_state_waitq;
 121    char *username;
 122    char *password;
 123    char *proxyusername;
 124    char *proxypassword;
 125} BDRVCURLState;
 126
 127static void curl_clean_state(CURLState *s);
 128static void curl_multi_do(void *arg);
 129
 130static gboolean curl_drop_socket(void *key, void *value, void *opaque)
 131{
 132    CURLSocket *socket = value;
 133    BDRVCURLState *s = socket->s;
 134
 135    aio_set_fd_handler(s->aio_context, socket->fd, false,
 136                       NULL, NULL, NULL, NULL, NULL);
 137    return true;
 138}
 139
 140static void curl_drop_all_sockets(GHashTable *sockets)
 141{
 142    g_hash_table_foreach_remove(sockets, curl_drop_socket, NULL);
 143}
 144
 145/* Called from curl_multi_do_locked, with s->mutex held.  */
 146static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque)
 147{
 148    BDRVCURLState *s = opaque;
 149
 150    trace_curl_timer_cb(timeout_ms);
 151    if (timeout_ms == -1) {
 152        timer_del(&s->timer);
 153    } else {
 154        int64_t timeout_ns = (int64_t)timeout_ms * 1000 * 1000;
 155        timer_mod(&s->timer,
 156                  qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + timeout_ns);
 157    }
 158    return 0;
 159}
 160
 161/* Called from curl_multi_do_locked, with s->mutex held.  */
 162static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
 163                        void *userp, void *sp)
 164{
 165    BDRVCURLState *s;
 166    CURLState *state = NULL;
 167    CURLSocket *socket;
 168
 169    curl_easy_getinfo(curl, CURLINFO_PRIVATE, (char **)&state);
 170    s = state->s;
 171
 172    socket = g_hash_table_lookup(s->sockets, GINT_TO_POINTER(fd));
 173    if (!socket) {
 174        socket = g_new0(CURLSocket, 1);
 175        socket->fd = fd;
 176        socket->s = s;
 177        g_hash_table_insert(s->sockets, GINT_TO_POINTER(fd), socket);
 178    }
 179
 180    trace_curl_sock_cb(action, (int)fd);
 181    switch (action) {
 182        case CURL_POLL_IN:
 183            aio_set_fd_handler(s->aio_context, fd, false,
 184                               curl_multi_do, NULL, NULL, NULL, socket);
 185            break;
 186        case CURL_POLL_OUT:
 187            aio_set_fd_handler(s->aio_context, fd, false,
 188                               NULL, curl_multi_do, NULL, NULL, socket);
 189            break;
 190        case CURL_POLL_INOUT:
 191            aio_set_fd_handler(s->aio_context, fd, false,
 192                               curl_multi_do, curl_multi_do,
 193                               NULL, NULL, socket);
 194            break;
 195        case CURL_POLL_REMOVE:
 196            aio_set_fd_handler(s->aio_context, fd, false,
 197                               NULL, NULL, NULL, NULL, NULL);
 198            break;
 199    }
 200
 201    if (action == CURL_POLL_REMOVE) {
 202        g_hash_table_remove(s->sockets, GINT_TO_POINTER(fd));
 203    }
 204
 205    return 0;
 206}
 207
 208/* Called from curl_multi_do_locked, with s->mutex held.  */
 209static size_t curl_header_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
 210{
 211    BDRVCURLState *s = opaque;
 212    size_t realsize = size * nmemb;
 213    const char *header = (char *)ptr;
 214    const char *end = header + realsize;
 215    const char *accept_ranges = "accept-ranges:";
 216    const char *bytes = "bytes";
 217
 218    if (realsize >= strlen(accept_ranges)
 219        && g_ascii_strncasecmp(header, accept_ranges,
 220                               strlen(accept_ranges)) == 0) {
 221
 222        char *p = strchr(header, ':') + 1;
 223
 224        /* Skip whitespace between the header name and value. */
 225        while (p < end && *p && g_ascii_isspace(*p)) {
 226            p++;
 227        }
 228
 229        if (end - p >= strlen(bytes)
 230            && strncmp(p, bytes, strlen(bytes)) == 0) {
 231
 232            /* Check that there is nothing but whitespace after the value. */
 233            p += strlen(bytes);
 234            while (p < end && *p && g_ascii_isspace(*p)) {
 235                p++;
 236            }
 237
 238            if (p == end || !*p) {
 239                s->accept_range = true;
 240            }
 241        }
 242    }
 243
 244    return realsize;
 245}
 246
 247/* Called from curl_multi_do_locked, with s->mutex held.  */
 248static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
 249{
 250    CURLState *s = ((CURLState*)opaque);
 251    size_t realsize = size * nmemb;
 252
 253    trace_curl_read_cb(realsize);
 254
 255    if (!s || !s->orig_buf) {
 256        goto read_end;
 257    }
 258
 259    if (s->buf_off >= s->buf_len) {
 260        /* buffer full, read nothing */
 261        goto read_end;
 262    }
 263    realsize = MIN(realsize, s->buf_len - s->buf_off);
 264    memcpy(s->orig_buf + s->buf_off, ptr, realsize);
 265    s->buf_off += realsize;
 266
 267read_end:
 268    /* curl will error out if we do not return this value */
 269    return size * nmemb;
 270}
 271
 272/* Called with s->mutex held.  */
 273static bool curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len,
 274                          CURLAIOCB *acb)
 275{
 276    int i;
 277    uint64_t end = start + len;
 278    uint64_t clamped_end = MIN(end, s->len);
 279    uint64_t clamped_len = clamped_end - start;
 280
 281    for (i=0; i<CURL_NUM_STATES; i++) {
 282        CURLState *state = &s->states[i];
 283        uint64_t buf_end = (state->buf_start + state->buf_off);
 284        uint64_t buf_fend = (state->buf_start + state->buf_len);
 285
 286        if (!state->orig_buf)
 287            continue;
 288        if (!state->buf_off)
 289            continue;
 290
 291        // Does the existing buffer cover our section?
 292        if ((start >= state->buf_start) &&
 293            (start <= buf_end) &&
 294            (clamped_end >= state->buf_start) &&
 295            (clamped_end <= buf_end))
 296        {
 297            char *buf = state->orig_buf + (start - state->buf_start);
 298
 299            qemu_iovec_from_buf(acb->qiov, 0, buf, clamped_len);
 300            if (clamped_len < len) {
 301                qemu_iovec_memset(acb->qiov, clamped_len, 0, len - clamped_len);
 302            }
 303            acb->ret = 0;
 304            return true;
 305        }
 306
 307        // Wait for unfinished chunks
 308        if (state->in_use &&
 309            (start >= state->buf_start) &&
 310            (start <= buf_fend) &&
 311            (clamped_end >= state->buf_start) &&
 312            (clamped_end <= buf_fend))
 313        {
 314            int j;
 315
 316            acb->start = start - state->buf_start;
 317            acb->end = acb->start + clamped_len;
 318
 319            for (j=0; j<CURL_NUM_ACB; j++) {
 320                if (!state->acb[j]) {
 321                    state->acb[j] = acb;
 322                    return true;
 323                }
 324            }
 325        }
 326    }
 327
 328    return false;
 329}
 330
 331/* Called with s->mutex held.  */
 332static void curl_multi_check_completion(BDRVCURLState *s)
 333{
 334    int msgs_in_queue;
 335
 336    /* Try to find done transfers, so we can free the easy
 337     * handle again. */
 338    for (;;) {
 339        CURLMsg *msg;
 340        msg = curl_multi_info_read(s->multi, &msgs_in_queue);
 341
 342        /* Quit when there are no more completions */
 343        if (!msg)
 344            break;
 345
 346        if (msg->msg == CURLMSG_DONE) {
 347            int i;
 348            CURLState *state = NULL;
 349            bool error = msg->data.result != CURLE_OK;
 350
 351            curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE,
 352                              (char **)&state);
 353
 354            if (error) {
 355                static int errcount = 100;
 356
 357                /* Don't lose the original error message from curl, since
 358                 * it contains extra data.
 359                 */
 360                if (errcount > 0) {
 361                    error_report("curl: %s", state->errmsg);
 362                    if (--errcount == 0) {
 363                        error_report("curl: further errors suppressed");
 364                    }
 365                }
 366            }
 367
 368            for (i = 0; i < CURL_NUM_ACB; i++) {
 369                CURLAIOCB *acb = state->acb[i];
 370
 371                if (acb == NULL) {
 372                    continue;
 373                }
 374
 375                if (!error) {
 376                    /* Assert that we have read all data */
 377                    assert(state->buf_off >= acb->end);
 378
 379                    qemu_iovec_from_buf(acb->qiov, 0,
 380                                        state->orig_buf + acb->start,
 381                                        acb->end - acb->start);
 382
 383                    if (acb->end - acb->start < acb->bytes) {
 384                        size_t offset = acb->end - acb->start;
 385                        qemu_iovec_memset(acb->qiov, offset, 0,
 386                                          acb->bytes - offset);
 387                    }
 388                }
 389
 390                acb->ret = error ? -EIO : 0;
 391                state->acb[i] = NULL;
 392                qemu_mutex_unlock(&s->mutex);
 393                aio_co_wake(acb->co);
 394                qemu_mutex_lock(&s->mutex);
 395            }
 396
 397            curl_clean_state(state);
 398            break;
 399        }
 400    }
 401}
 402
 403/* Called with s->mutex held.  */
 404static void curl_multi_do_locked(CURLSocket *socket)
 405{
 406    BDRVCURLState *s = socket->s;
 407    int running;
 408    int r;
 409
 410    if (!s->multi) {
 411        return;
 412    }
 413
 414    do {
 415        r = curl_multi_socket_action(s->multi, socket->fd, 0, &running);
 416    } while (r == CURLM_CALL_MULTI_PERFORM);
 417}
 418
 419static void curl_multi_do(void *arg)
 420{
 421    CURLSocket *socket = arg;
 422    BDRVCURLState *s = socket->s;
 423
 424    qemu_mutex_lock(&s->mutex);
 425    curl_multi_do_locked(socket);
 426    curl_multi_check_completion(s);
 427    qemu_mutex_unlock(&s->mutex);
 428}
 429
 430static void curl_multi_timeout_do(void *arg)
 431{
 432    BDRVCURLState *s = (BDRVCURLState *)arg;
 433    int running;
 434
 435    if (!s->multi) {
 436        return;
 437    }
 438
 439    qemu_mutex_lock(&s->mutex);
 440    curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
 441
 442    curl_multi_check_completion(s);
 443    qemu_mutex_unlock(&s->mutex);
 444}
 445
 446/* Called with s->mutex held.  */
 447static CURLState *curl_find_state(BDRVCURLState *s)
 448{
 449    CURLState *state = NULL;
 450    int i;
 451
 452    for (i = 0; i < CURL_NUM_STATES; i++) {
 453        if (!s->states[i].in_use) {
 454            state = &s->states[i];
 455            state->in_use = 1;
 456            break;
 457        }
 458    }
 459    return state;
 460}
 461
 462static int curl_init_state(BDRVCURLState *s, CURLState *state)
 463{
 464    if (!state->curl) {
 465        state->curl = curl_easy_init();
 466        if (!state->curl) {
 467            return -EIO;
 468        }
 469        if (curl_easy_setopt(state->curl, CURLOPT_URL, s->url) ||
 470            curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
 471                             (long) s->sslverify) ||
 472            curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYHOST,
 473                             s->sslverify ? 2L : 0L)) {
 474            goto err;
 475        }
 476        if (s->cookie) {
 477            if (curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie)) {
 478                goto err;
 479            }
 480        }
 481        if (curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, (long)s->timeout) ||
 482            curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION,
 483                             (void *)curl_read_cb) ||
 484            curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state) ||
 485            curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state) ||
 486            curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1) ||
 487            curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1) ||
 488            curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1) ||
 489            curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg) ||
 490            curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1)) {
 491            goto err;
 492        }
 493        if (s->username) {
 494            if (curl_easy_setopt(state->curl, CURLOPT_USERNAME, s->username)) {
 495                goto err;
 496            }
 497        }
 498        if (s->password) {
 499            if (curl_easy_setopt(state->curl, CURLOPT_PASSWORD, s->password)) {
 500                goto err;
 501            }
 502        }
 503        if (s->proxyusername) {
 504            if (curl_easy_setopt(state->curl,
 505                                 CURLOPT_PROXYUSERNAME, s->proxyusername)) {
 506                goto err;
 507            }
 508        }
 509        if (s->proxypassword) {
 510            if (curl_easy_setopt(state->curl,
 511                                 CURLOPT_PROXYPASSWORD, s->proxypassword)) {
 512                goto err;
 513            }
 514        }
 515
 516        /* Restrict supported protocols to avoid security issues in the more
 517         * obscure protocols.  For example, do not allow POP3/SMTP/IMAP see
 518         * CVE-2013-0249.
 519         *
 520         * Restricting protocols is only supported from 7.19.4 upwards. Note:
 521         * version 7.85.0 deprecates CURLOPT_*PROTOCOLS in favour of a string
 522         * based CURLOPT_*PROTOCOLS_STR API.
 523         */
 524#if LIBCURL_VERSION_NUM >= 0x075500
 525        if (curl_easy_setopt(state->curl,
 526                             CURLOPT_PROTOCOLS_STR, PROTOCOLS) ||
 527            curl_easy_setopt(state->curl,
 528                             CURLOPT_REDIR_PROTOCOLS_STR, PROTOCOLS)) {
 529            goto err;
 530        }
 531#elif LIBCURL_VERSION_NUM >= 0x071304
 532        if (curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS) ||
 533            curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS)) {
 534            goto err;
 535        }
 536#endif
 537
 538#ifdef DEBUG_VERBOSE
 539        if (curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1)) {
 540            goto err;
 541        }
 542#endif
 543    }
 544
 545    state->s = s;
 546
 547    return 0;
 548
 549err:
 550    curl_easy_cleanup(state->curl);
 551    state->curl = NULL;
 552    return -EIO;
 553}
 554
 555/* Called with s->mutex held.  */
 556static void curl_clean_state(CURLState *s)
 557{
 558    int j;
 559    for (j = 0; j < CURL_NUM_ACB; j++) {
 560        assert(!s->acb[j]);
 561    }
 562
 563    if (s->s->multi)
 564        curl_multi_remove_handle(s->s->multi, s->curl);
 565
 566    s->in_use = 0;
 567
 568    qemu_co_enter_next(&s->s->free_state_waitq, &s->s->mutex);
 569}
 570
 571static void curl_parse_filename(const char *filename, QDict *options,
 572                                Error **errp)
 573{
 574    qdict_put_str(options, CURL_BLOCK_OPT_URL, filename);
 575}
 576
 577static void curl_detach_aio_context(BlockDriverState *bs)
 578{
 579    BDRVCURLState *s = bs->opaque;
 580    int i;
 581
 582    WITH_QEMU_LOCK_GUARD(&s->mutex) {
 583        curl_drop_all_sockets(s->sockets);
 584        for (i = 0; i < CURL_NUM_STATES; i++) {
 585            if (s->states[i].in_use) {
 586                curl_clean_state(&s->states[i]);
 587            }
 588            if (s->states[i].curl) {
 589                curl_easy_cleanup(s->states[i].curl);
 590                s->states[i].curl = NULL;
 591            }
 592            g_free(s->states[i].orig_buf);
 593            s->states[i].orig_buf = NULL;
 594        }
 595        if (s->multi) {
 596            curl_multi_cleanup(s->multi);
 597            s->multi = NULL;
 598        }
 599    }
 600
 601    timer_del(&s->timer);
 602}
 603
 604static void curl_attach_aio_context(BlockDriverState *bs,
 605                                    AioContext *new_context)
 606{
 607    BDRVCURLState *s = bs->opaque;
 608
 609    aio_timer_init(new_context, &s->timer,
 610                   QEMU_CLOCK_REALTIME, SCALE_NS,
 611                   curl_multi_timeout_do, s);
 612
 613    assert(!s->multi);
 614    s->multi = curl_multi_init();
 615    s->aio_context = new_context;
 616    curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb);
 617    curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s);
 618    curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb);
 619}
 620
 621static QemuOptsList runtime_opts = {
 622    .name = "curl",
 623    .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
 624    .desc = {
 625        {
 626            .name = CURL_BLOCK_OPT_URL,
 627            .type = QEMU_OPT_STRING,
 628            .help = "URL to open",
 629        },
 630        {
 631            .name = CURL_BLOCK_OPT_READAHEAD,
 632            .type = QEMU_OPT_SIZE,
 633            .help = "Readahead size",
 634        },
 635        {
 636            .name = CURL_BLOCK_OPT_SSLVERIFY,
 637            .type = QEMU_OPT_BOOL,
 638            .help = "Verify SSL certificate"
 639        },
 640        {
 641            .name = CURL_BLOCK_OPT_TIMEOUT,
 642            .type = QEMU_OPT_NUMBER,
 643            .help = "Curl timeout"
 644        },
 645        {
 646            .name = CURL_BLOCK_OPT_COOKIE,
 647            .type = QEMU_OPT_STRING,
 648            .help = "Pass the cookie or list of cookies with each request"
 649        },
 650        {
 651            .name = CURL_BLOCK_OPT_COOKIE_SECRET,
 652            .type = QEMU_OPT_STRING,
 653            .help = "ID of secret used as cookie passed with each request"
 654        },
 655        {
 656            .name = CURL_BLOCK_OPT_USERNAME,
 657            .type = QEMU_OPT_STRING,
 658            .help = "Username for HTTP auth"
 659        },
 660        {
 661            .name = CURL_BLOCK_OPT_PASSWORD_SECRET,
 662            .type = QEMU_OPT_STRING,
 663            .help = "ID of secret used as password for HTTP auth",
 664        },
 665        {
 666            .name = CURL_BLOCK_OPT_PROXY_USERNAME,
 667            .type = QEMU_OPT_STRING,
 668            .help = "Username for HTTP proxy auth"
 669        },
 670        {
 671            .name = CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET,
 672            .type = QEMU_OPT_STRING,
 673            .help = "ID of secret used as password for HTTP proxy auth",
 674        },
 675        { /* end of list */ }
 676    },
 677};
 678
 679
 680static int curl_open(BlockDriverState *bs, QDict *options, int flags,
 681                     Error **errp)
 682{
 683    BDRVCURLState *s = bs->opaque;
 684    CURLState *state = NULL;
 685    QemuOpts *opts;
 686    const char *file;
 687    const char *cookie;
 688    const char *cookie_secret;
 689    /* CURL >= 7.55.0 uses curl_off_t for content length instead of a double */
 690#if LIBCURL_VERSION_NUM >= 0x073700
 691    curl_off_t cl;
 692#else
 693    double cl;
 694#endif
 695    const char *secretid;
 696    const char *protocol_delimiter;
 697    int ret;
 698
 699    ret = bdrv_apply_auto_read_only(bs, "curl driver does not support writes",
 700                                    errp);
 701    if (ret < 0) {
 702        return ret;
 703    }
 704
 705    if (!libcurl_initialized) {
 706        ret = curl_global_init(CURL_GLOBAL_ALL);
 707        if (ret) {
 708            error_setg(errp, "libcurl initialization failed with %d", ret);
 709            return -EIO;
 710        }
 711        libcurl_initialized = true;
 712    }
 713
 714    qemu_mutex_init(&s->mutex);
 715    opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
 716    if (!qemu_opts_absorb_qdict(opts, options, errp)) {
 717        goto out_noclean;
 718    }
 719
 720    s->readahead_size = qemu_opt_get_size(opts, CURL_BLOCK_OPT_READAHEAD,
 721                                          CURL_BLOCK_OPT_READAHEAD_DEFAULT);
 722    if ((s->readahead_size & 0x1ff) != 0) {
 723        error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512",
 724                   s->readahead_size);
 725        goto out_noclean;
 726    }
 727
 728    s->timeout = qemu_opt_get_number(opts, CURL_BLOCK_OPT_TIMEOUT,
 729                                     CURL_BLOCK_OPT_TIMEOUT_DEFAULT);
 730    if (s->timeout > CURL_TIMEOUT_MAX) {
 731        error_setg(errp, "timeout parameter is too large or negative");
 732        goto out_noclean;
 733    }
 734
 735    s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY,
 736                                     CURL_BLOCK_OPT_SSLVERIFY_DEFAULT);
 737
 738    cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE);
 739    cookie_secret = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE_SECRET);
 740
 741    if (cookie && cookie_secret) {
 742        error_setg(errp,
 743                   "curl driver cannot handle both cookie and cookie secret");
 744        goto out_noclean;
 745    }
 746
 747    if (cookie_secret) {
 748        s->cookie = qcrypto_secret_lookup_as_utf8(cookie_secret, errp);
 749        if (!s->cookie) {
 750            goto out_noclean;
 751        }
 752    } else {
 753        s->cookie = g_strdup(cookie);
 754    }
 755
 756    file = qemu_opt_get(opts, CURL_BLOCK_OPT_URL);
 757    if (file == NULL) {
 758        error_setg(errp, "curl block driver requires an 'url' option");
 759        goto out_noclean;
 760    }
 761
 762    if (!strstart(file, bs->drv->protocol_name, &protocol_delimiter) ||
 763        !strstart(protocol_delimiter, "://", NULL))
 764    {
 765        error_setg(errp, "%s curl driver cannot handle the URL '%s' (does not "
 766                   "start with '%s://')", bs->drv->protocol_name, file,
 767                   bs->drv->protocol_name);
 768        goto out_noclean;
 769    }
 770
 771    s->username = g_strdup(qemu_opt_get(opts, CURL_BLOCK_OPT_USERNAME));
 772    secretid = qemu_opt_get(opts, CURL_BLOCK_OPT_PASSWORD_SECRET);
 773
 774    if (secretid) {
 775        s->password = qcrypto_secret_lookup_as_utf8(secretid, errp);
 776        if (!s->password) {
 777            goto out_noclean;
 778        }
 779    }
 780
 781    s->proxyusername = g_strdup(
 782        qemu_opt_get(opts, CURL_BLOCK_OPT_PROXY_USERNAME));
 783    secretid = qemu_opt_get(opts, CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET);
 784    if (secretid) {
 785        s->proxypassword = qcrypto_secret_lookup_as_utf8(secretid, errp);
 786        if (!s->proxypassword) {
 787            goto out_noclean;
 788        }
 789    }
 790
 791    trace_curl_open(file);
 792    qemu_co_queue_init(&s->free_state_waitq);
 793    s->aio_context = bdrv_get_aio_context(bs);
 794    s->url = g_strdup(file);
 795    s->sockets = g_hash_table_new_full(NULL, NULL, NULL, g_free);
 796    qemu_mutex_lock(&s->mutex);
 797    state = curl_find_state(s);
 798    qemu_mutex_unlock(&s->mutex);
 799    if (!state) {
 800        goto out_noclean;
 801    }
 802
 803    // Get file size
 804
 805    if (curl_init_state(s, state) < 0) {
 806        pstrcpy(state->errmsg, CURL_ERROR_SIZE,
 807                "curl library initialization failed.");
 808        goto out;
 809    }
 810
 811    s->accept_range = false;
 812    if (curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1) ||
 813        curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION, curl_header_cb) ||
 814        curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s)) {
 815        pstrcpy(state->errmsg, CURL_ERROR_SIZE,
 816                "curl library initialization failed.");
 817        goto out;
 818    }
 819    if (curl_easy_perform(state->curl))
 820        goto out;
 821    /* CURL 7.55.0 deprecates CURLINFO_CONTENT_LENGTH_DOWNLOAD in favour of
 822     * the *_T version which returns a more sensible type for content length.
 823     */
 824#if LIBCURL_VERSION_NUM >= 0x073700
 825    if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &cl)) {
 826        goto out;
 827    }
 828#else
 829    if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &cl)) {
 830        goto out;
 831    }
 832#endif
 833    /* Prior CURL 7.19.4 return value of 0 could mean that the file size is not
 834     * know or the size is zero. From 7.19.4 CURL returns -1 if size is not
 835     * known and zero if it is really zero-length file. */
 836#if LIBCURL_VERSION_NUM >= 0x071304
 837    if (cl < 0) {
 838        pstrcpy(state->errmsg, CURL_ERROR_SIZE,
 839                "Server didn't report file size.");
 840        goto out;
 841    }
 842#else
 843    if (cl <= 0) {
 844        pstrcpy(state->errmsg, CURL_ERROR_SIZE,
 845                "Unknown file size or zero-length file.");
 846        goto out;
 847    }
 848#endif
 849
 850    s->len = cl;
 851
 852    if ((!strncasecmp(s->url, "http://", strlen("http://"))
 853        || !strncasecmp(s->url, "https://", strlen("https://")))
 854        && !s->accept_range) {
 855        pstrcpy(state->errmsg, CURL_ERROR_SIZE,
 856                "Server does not support 'range' (byte ranges).");
 857        goto out;
 858    }
 859    trace_curl_open_size(s->len);
 860
 861    qemu_mutex_lock(&s->mutex);
 862    curl_clean_state(state);
 863    qemu_mutex_unlock(&s->mutex);
 864    curl_easy_cleanup(state->curl);
 865    state->curl = NULL;
 866
 867    curl_attach_aio_context(bs, bdrv_get_aio_context(bs));
 868
 869    qemu_opts_del(opts);
 870    return 0;
 871
 872out:
 873    error_setg(errp, "CURL: Error opening file: %s", state->errmsg);
 874    curl_easy_cleanup(state->curl);
 875    state->curl = NULL;
 876out_noclean:
 877    qemu_mutex_destroy(&s->mutex);
 878    g_free(s->cookie);
 879    g_free(s->url);
 880    g_free(s->username);
 881    g_free(s->proxyusername);
 882    g_free(s->proxypassword);
 883    if (s->sockets) {
 884        curl_drop_all_sockets(s->sockets);
 885        g_hash_table_destroy(s->sockets);
 886    }
 887    qemu_opts_del(opts);
 888    return -EINVAL;
 889}
 890
 891static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
 892{
 893    CURLState *state;
 894    int running;
 895
 896    BDRVCURLState *s = bs->opaque;
 897
 898    uint64_t start = acb->offset;
 899    uint64_t end;
 900
 901    qemu_mutex_lock(&s->mutex);
 902
 903    // In case we have the requested data already (e.g. read-ahead),
 904    // we can just call the callback and be done.
 905    if (curl_find_buf(s, start, acb->bytes, acb)) {
 906        goto out;
 907    }
 908
 909    // No cache found, so let's start a new request
 910    for (;;) {
 911        state = curl_find_state(s);
 912        if (state) {
 913            break;
 914        }
 915        qemu_co_queue_wait(&s->free_state_waitq, &s->mutex);
 916    }
 917
 918    if (curl_init_state(s, state) < 0) {
 919        curl_clean_state(state);
 920        acb->ret = -EIO;
 921        goto out;
 922    }
 923
 924    acb->start = 0;
 925    acb->end = MIN(acb->bytes, s->len - start);
 926
 927    state->buf_off = 0;
 928    g_free(state->orig_buf);
 929    state->buf_start = start;
 930    state->buf_len = MIN(acb->end + s->readahead_size, s->len - start);
 931    end = start + state->buf_len - 1;
 932    state->orig_buf = g_try_malloc(state->buf_len);
 933    if (state->buf_len && state->orig_buf == NULL) {
 934        curl_clean_state(state);
 935        acb->ret = -ENOMEM;
 936        goto out;
 937    }
 938    state->acb[0] = acb;
 939
 940    snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end);
 941    trace_curl_setup_preadv(acb->bytes, start, state->range);
 942    if (curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range) ||
 943        curl_multi_add_handle(s->multi, state->curl) != CURLM_OK) {
 944        state->acb[0] = NULL;
 945        acb->ret = -EIO;
 946
 947        curl_clean_state(state);
 948        goto out;
 949    }
 950
 951    /* Tell curl it needs to kick things off */
 952    curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
 953
 954out:
 955    qemu_mutex_unlock(&s->mutex);
 956}
 957
 958static int coroutine_fn curl_co_preadv(BlockDriverState *bs,
 959        int64_t offset, int64_t bytes, QEMUIOVector *qiov,
 960        BdrvRequestFlags flags)
 961{
 962    CURLAIOCB acb = {
 963        .co = qemu_coroutine_self(),
 964        .ret = -EINPROGRESS,
 965        .qiov = qiov,
 966        .offset = offset,
 967        .bytes = bytes
 968    };
 969
 970    curl_setup_preadv(bs, &acb);
 971    while (acb.ret == -EINPROGRESS) {
 972        qemu_coroutine_yield();
 973    }
 974    return acb.ret;
 975}
 976
 977static void curl_close(BlockDriverState *bs)
 978{
 979    BDRVCURLState *s = bs->opaque;
 980
 981    trace_curl_close();
 982    curl_detach_aio_context(bs);
 983    qemu_mutex_destroy(&s->mutex);
 984
 985    g_hash_table_destroy(s->sockets);
 986    g_free(s->cookie);
 987    g_free(s->url);
 988    g_free(s->username);
 989    g_free(s->proxyusername);
 990    g_free(s->proxypassword);
 991}
 992
 993static int64_t coroutine_fn curl_co_getlength(BlockDriverState *bs)
 994{
 995    BDRVCURLState *s = bs->opaque;
 996    return s->len;
 997}
 998
 999static void curl_refresh_filename(BlockDriverState *bs)
1000{
1001    BDRVCURLState *s = bs->opaque;
1002
1003    /* "readahead" and "timeout" do not change the guest-visible data,
1004     * so ignore them */
1005    if (s->sslverify != CURL_BLOCK_OPT_SSLVERIFY_DEFAULT ||
1006        s->cookie || s->username || s->password || s->proxyusername ||
1007        s->proxypassword)
1008    {
1009        return;
1010    }
1011
1012    pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), s->url);
1013}
1014
1015
1016static const char *const curl_strong_runtime_opts[] = {
1017    CURL_BLOCK_OPT_URL,
1018    CURL_BLOCK_OPT_SSLVERIFY,
1019    CURL_BLOCK_OPT_COOKIE,
1020    CURL_BLOCK_OPT_COOKIE_SECRET,
1021    CURL_BLOCK_OPT_USERNAME,
1022    CURL_BLOCK_OPT_PASSWORD_SECRET,
1023    CURL_BLOCK_OPT_PROXY_USERNAME,
1024    CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET,
1025
1026    NULL
1027};
1028
1029static BlockDriver bdrv_http = {
1030    .format_name                = "http",
1031    .protocol_name              = "http",
1032
1033    .instance_size              = sizeof(BDRVCURLState),
1034    .bdrv_parse_filename        = curl_parse_filename,
1035    .bdrv_file_open             = curl_open,
1036    .bdrv_close                 = curl_close,
1037    .bdrv_co_getlength          = curl_co_getlength,
1038
1039    .bdrv_co_preadv             = curl_co_preadv,
1040
1041    .bdrv_detach_aio_context    = curl_detach_aio_context,
1042    .bdrv_attach_aio_context    = curl_attach_aio_context,
1043
1044    .bdrv_refresh_filename      = curl_refresh_filename,
1045    .strong_runtime_opts        = curl_strong_runtime_opts,
1046};
1047
1048static BlockDriver bdrv_https = {
1049    .format_name                = "https",
1050    .protocol_name              = "https",
1051
1052    .instance_size              = sizeof(BDRVCURLState),
1053    .bdrv_parse_filename        = curl_parse_filename,
1054    .bdrv_file_open             = curl_open,
1055    .bdrv_close                 = curl_close,
1056    .bdrv_co_getlength          = curl_co_getlength,
1057
1058    .bdrv_co_preadv             = curl_co_preadv,
1059
1060    .bdrv_detach_aio_context    = curl_detach_aio_context,
1061    .bdrv_attach_aio_context    = curl_attach_aio_context,
1062
1063    .bdrv_refresh_filename      = curl_refresh_filename,
1064    .strong_runtime_opts        = curl_strong_runtime_opts,
1065};
1066
1067static BlockDriver bdrv_ftp = {
1068    .format_name                = "ftp",
1069    .protocol_name              = "ftp",
1070
1071    .instance_size              = sizeof(BDRVCURLState),
1072    .bdrv_parse_filename        = curl_parse_filename,
1073    .bdrv_file_open             = curl_open,
1074    .bdrv_close                 = curl_close,
1075    .bdrv_co_getlength          = curl_co_getlength,
1076
1077    .bdrv_co_preadv             = curl_co_preadv,
1078
1079    .bdrv_detach_aio_context    = curl_detach_aio_context,
1080    .bdrv_attach_aio_context    = curl_attach_aio_context,
1081
1082    .bdrv_refresh_filename      = curl_refresh_filename,
1083    .strong_runtime_opts        = curl_strong_runtime_opts,
1084};
1085
1086static BlockDriver bdrv_ftps = {
1087    .format_name                = "ftps",
1088    .protocol_name              = "ftps",
1089
1090    .instance_size              = sizeof(BDRVCURLState),
1091    .bdrv_parse_filename        = curl_parse_filename,
1092    .bdrv_file_open             = curl_open,
1093    .bdrv_close                 = curl_close,
1094    .bdrv_co_getlength          = curl_co_getlength,
1095
1096    .bdrv_co_preadv             = curl_co_preadv,
1097
1098    .bdrv_detach_aio_context    = curl_detach_aio_context,
1099    .bdrv_attach_aio_context    = curl_attach_aio_context,
1100
1101    .bdrv_refresh_filename      = curl_refresh_filename,
1102    .strong_runtime_opts        = curl_strong_runtime_opts,
1103};
1104
1105static void curl_block_init(void)
1106{
1107    bdrv_register(&bdrv_http);
1108    bdrv_register(&bdrv_https);
1109    bdrv_register(&bdrv_ftp);
1110    bdrv_register(&bdrv_ftps);
1111}
1112
1113block_init(curl_block_init);
1114