qemu

Форк
0
/
curl.c 
1107 строк · 32.1 Кб
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

70
struct BDRVCURLState;
71
struct CURLState;
72

73
static bool libcurl_initialized;
74

75
typedef 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

87
typedef struct CURLSocket {
88
    int fd;
89
    struct BDRVCURLState *s;
90
} CURLSocket;
91

92
typedef 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

106
typedef 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

127
static void curl_clean_state(CURLState *s);
128
static void curl_multi_do(void *arg);
129

130
static 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,
136
                       NULL, NULL, NULL, NULL, NULL);
137
    return true;
138
}
139

140
static 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.  */
146
static 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.  */
162
static 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,
184
                               curl_multi_do, NULL, NULL, NULL, socket);
185
            break;
186
        case CURL_POLL_OUT:
187
            aio_set_fd_handler(s->aio_context, fd,
188
                               NULL, curl_multi_do, NULL, NULL, socket);
189
            break;
190
        case CURL_POLL_INOUT:
191
            aio_set_fd_handler(s->aio_context, fd,
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,
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.  */
209
static 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 *p = ptr;
214
    const char *end = p + realsize;
215
    const char *t = "accept-ranges : bytes "; /* A lowercase template */
216

217
    /* check if header matches the "t" template */
218
    for (;;) {
219
        if (*t == ' ') { /* space in t matches any amount of isspace in p */
220
            if (p < end && g_ascii_isspace(*p)) {
221
                ++p;
222
            } else {
223
                ++t;
224
            }
225
        } else if (*t && p < end && *t == g_ascii_tolower(*p)) {
226
            ++p, ++t;
227
        } else {
228
            break;
229
        }
230
    }
231

232
    if (!*t && p == end) { /* if we managed to reach ends of both strings */
233
        s->accept_range = true;
234
    }
235

236
    return realsize;
237
}
238

239
/* Called from curl_multi_do_locked, with s->mutex held.  */
240
static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
241
{
242
    CURLState *s = ((CURLState*)opaque);
243
    size_t realsize = size * nmemb;
244

245
    trace_curl_read_cb(realsize);
246

247
    if (!s || !s->orig_buf) {
248
        goto read_end;
249
    }
250

251
    if (s->buf_off >= s->buf_len) {
252
        /* buffer full, read nothing */
253
        goto read_end;
254
    }
255
    realsize = MIN(realsize, s->buf_len - s->buf_off);
256
    memcpy(s->orig_buf + s->buf_off, ptr, realsize);
257
    s->buf_off += realsize;
258

259
read_end:
260
    /* curl will error out if we do not return this value */
261
    return size * nmemb;
262
}
263

264
/* Called with s->mutex held.  */
265
static bool curl_find_buf(BDRVCURLState *s, uint64_t start, uint64_t len,
266
                          CURLAIOCB *acb)
267
{
268
    int i;
269
    uint64_t end = start + len;
270
    uint64_t clamped_end = MIN(end, s->len);
271
    uint64_t clamped_len = clamped_end - start;
272

273
    for (i=0; i<CURL_NUM_STATES; i++) {
274
        CURLState *state = &s->states[i];
275
        uint64_t buf_end = (state->buf_start + state->buf_off);
276
        uint64_t buf_fend = (state->buf_start + state->buf_len);
277

278
        if (!state->orig_buf)
279
            continue;
280
        if (!state->buf_off)
281
            continue;
282

283
        // Does the existing buffer cover our section?
284
        if ((start >= state->buf_start) &&
285
            (start <= buf_end) &&
286
            (clamped_end >= state->buf_start) &&
287
            (clamped_end <= buf_end))
288
        {
289
            char *buf = state->orig_buf + (start - state->buf_start);
290

291
            qemu_iovec_from_buf(acb->qiov, 0, buf, clamped_len);
292
            if (clamped_len < len) {
293
                qemu_iovec_memset(acb->qiov, clamped_len, 0, len - clamped_len);
294
            }
295
            acb->ret = 0;
296
            return true;
297
        }
298

299
        // Wait for unfinished chunks
300
        if (state->in_use &&
301
            (start >= state->buf_start) &&
302
            (start <= buf_fend) &&
303
            (clamped_end >= state->buf_start) &&
304
            (clamped_end <= buf_fend))
305
        {
306
            int j;
307

308
            acb->start = start - state->buf_start;
309
            acb->end = acb->start + clamped_len;
310

311
            for (j=0; j<CURL_NUM_ACB; j++) {
312
                if (!state->acb[j]) {
313
                    state->acb[j] = acb;
314
                    return true;
315
                }
316
            }
317
        }
318
    }
319

320
    return false;
321
}
322

323
/* Called with s->mutex held.  */
324
static void curl_multi_check_completion(BDRVCURLState *s)
325
{
326
    int msgs_in_queue;
327

328
    /* Try to find done transfers, so we can free the easy
329
     * handle again. */
330
    for (;;) {
331
        CURLMsg *msg;
332
        msg = curl_multi_info_read(s->multi, &msgs_in_queue);
333

334
        /* Quit when there are no more completions */
335
        if (!msg)
336
            break;
337

338
        if (msg->msg == CURLMSG_DONE) {
339
            int i;
340
            CURLState *state = NULL;
341
            bool error = msg->data.result != CURLE_OK;
342

343
            curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE,
344
                              (char **)&state);
345

346
            if (error) {
347
                static int errcount = 100;
348

349
                /* Don't lose the original error message from curl, since
350
                 * it contains extra data.
351
                 */
352
                if (errcount > 0) {
353
                    error_report("curl: %s", state->errmsg);
354
                    if (--errcount == 0) {
355
                        error_report("curl: further errors suppressed");
356
                    }
357
                }
358
            }
359

360
            for (i = 0; i < CURL_NUM_ACB; i++) {
361
                CURLAIOCB *acb = state->acb[i];
362

363
                if (acb == NULL) {
364
                    continue;
365
                }
366

367
                if (!error) {
368
                    /* Assert that we have read all data */
369
                    assert(state->buf_off >= acb->end);
370

371
                    qemu_iovec_from_buf(acb->qiov, 0,
372
                                        state->orig_buf + acb->start,
373
                                        acb->end - acb->start);
374

375
                    if (acb->end - acb->start < acb->bytes) {
376
                        size_t offset = acb->end - acb->start;
377
                        qemu_iovec_memset(acb->qiov, offset, 0,
378
                                          acb->bytes - offset);
379
                    }
380
                }
381

382
                acb->ret = error ? -EIO : 0;
383
                state->acb[i] = NULL;
384
                qemu_mutex_unlock(&s->mutex);
385
                aio_co_wake(acb->co);
386
                qemu_mutex_lock(&s->mutex);
387
            }
388

389
            curl_clean_state(state);
390
            break;
391
        }
392
    }
393
}
394

395
/* Called with s->mutex held.  */
396
static void curl_multi_do_locked(CURLSocket *socket)
397
{
398
    BDRVCURLState *s = socket->s;
399
    int running;
400
    int r;
401

402
    if (!s->multi) {
403
        return;
404
    }
405

406
    do {
407
        r = curl_multi_socket_action(s->multi, socket->fd, 0, &running);
408
    } while (r == CURLM_CALL_MULTI_PERFORM);
409
}
410

411
static void curl_multi_do(void *arg)
412
{
413
    CURLSocket *socket = arg;
414
    BDRVCURLState *s = socket->s;
415

416
    qemu_mutex_lock(&s->mutex);
417
    curl_multi_do_locked(socket);
418
    curl_multi_check_completion(s);
419
    qemu_mutex_unlock(&s->mutex);
420
}
421

422
static void curl_multi_timeout_do(void *arg)
423
{
424
    BDRVCURLState *s = (BDRVCURLState *)arg;
425
    int running;
426

427
    if (!s->multi) {
428
        return;
429
    }
430

431
    qemu_mutex_lock(&s->mutex);
432
    curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
433

434
    curl_multi_check_completion(s);
435
    qemu_mutex_unlock(&s->mutex);
436
}
437

438
/* Called with s->mutex held.  */
439
static CURLState *curl_find_state(BDRVCURLState *s)
440
{
441
    CURLState *state = NULL;
442
    int i;
443

444
    for (i = 0; i < CURL_NUM_STATES; i++) {
445
        if (!s->states[i].in_use) {
446
            state = &s->states[i];
447
            state->in_use = 1;
448
            break;
449
        }
450
    }
451
    return state;
452
}
453

454
static int curl_init_state(BDRVCURLState *s, CURLState *state)
455
{
456
    if (!state->curl) {
457
        state->curl = curl_easy_init();
458
        if (!state->curl) {
459
            return -EIO;
460
        }
461
        if (curl_easy_setopt(state->curl, CURLOPT_URL, s->url) ||
462
            curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER,
463
                             (long) s->sslverify) ||
464
            curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYHOST,
465
                             s->sslverify ? 2L : 0L)) {
466
            goto err;
467
        }
468
        if (s->cookie) {
469
            if (curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie)) {
470
                goto err;
471
            }
472
        }
473
        if (curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, (long)s->timeout) ||
474
            curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION,
475
                             (void *)curl_read_cb) ||
476
            curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state) ||
477
            curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state) ||
478
            curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1) ||
479
            curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1) ||
480
            curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1) ||
481
            curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg) ||
482
            curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1)) {
483
            goto err;
484
        }
485
        if (s->username) {
486
            if (curl_easy_setopt(state->curl, CURLOPT_USERNAME, s->username)) {
487
                goto err;
488
            }
489
        }
490
        if (s->password) {
491
            if (curl_easy_setopt(state->curl, CURLOPT_PASSWORD, s->password)) {
492
                goto err;
493
            }
494
        }
495
        if (s->proxyusername) {
496
            if (curl_easy_setopt(state->curl,
497
                                 CURLOPT_PROXYUSERNAME, s->proxyusername)) {
498
                goto err;
499
            }
500
        }
501
        if (s->proxypassword) {
502
            if (curl_easy_setopt(state->curl,
503
                                 CURLOPT_PROXYPASSWORD, s->proxypassword)) {
504
                goto err;
505
            }
506
        }
507

508
        /* Restrict supported protocols to avoid security issues in the more
509
         * obscure protocols.  For example, do not allow POP3/SMTP/IMAP see
510
         * CVE-2013-0249.
511
         *
512
         * Restricting protocols is only supported from 7.19.4 upwards. Note:
513
         * version 7.85.0 deprecates CURLOPT_*PROTOCOLS in favour of a string
514
         * based CURLOPT_*PROTOCOLS_STR API.
515
         */
516
#if LIBCURL_VERSION_NUM >= 0x075500
517
        if (curl_easy_setopt(state->curl,
518
                             CURLOPT_PROTOCOLS_STR, PROTOCOLS) ||
519
            curl_easy_setopt(state->curl,
520
                             CURLOPT_REDIR_PROTOCOLS_STR, PROTOCOLS)) {
521
            goto err;
522
        }
523
#elif LIBCURL_VERSION_NUM >= 0x071304
524
        if (curl_easy_setopt(state->curl, CURLOPT_PROTOCOLS, PROTOCOLS) ||
525
            curl_easy_setopt(state->curl, CURLOPT_REDIR_PROTOCOLS, PROTOCOLS)) {
526
            goto err;
527
        }
528
#endif
529

530
#ifdef DEBUG_VERBOSE
531
        if (curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1)) {
532
            goto err;
533
        }
534
#endif
535
    }
536

537
    state->s = s;
538

539
    return 0;
540

541
err:
542
    curl_easy_cleanup(state->curl);
543
    state->curl = NULL;
544
    return -EIO;
545
}
546

547
/* Called with s->mutex held.  */
548
static void curl_clean_state(CURLState *s)
549
{
550
    int j;
551
    for (j = 0; j < CURL_NUM_ACB; j++) {
552
        assert(!s->acb[j]);
553
    }
554

555
    if (s->s->multi)
556
        curl_multi_remove_handle(s->s->multi, s->curl);
557

558
    s->in_use = 0;
559

560
    qemu_co_enter_next(&s->s->free_state_waitq, &s->s->mutex);
561
}
562

563
static void curl_parse_filename(const char *filename, QDict *options,
564
                                Error **errp)
565
{
566
    qdict_put_str(options, CURL_BLOCK_OPT_URL, filename);
567
}
568

569
static void curl_detach_aio_context(BlockDriverState *bs)
570
{
571
    BDRVCURLState *s = bs->opaque;
572
    int i;
573

574
    WITH_QEMU_LOCK_GUARD(&s->mutex) {
575
        curl_drop_all_sockets(s->sockets);
576
        for (i = 0; i < CURL_NUM_STATES; i++) {
577
            if (s->states[i].in_use) {
578
                curl_clean_state(&s->states[i]);
579
            }
580
            if (s->states[i].curl) {
581
                curl_easy_cleanup(s->states[i].curl);
582
                s->states[i].curl = NULL;
583
            }
584
            g_free(s->states[i].orig_buf);
585
            s->states[i].orig_buf = NULL;
586
        }
587
        if (s->multi) {
588
            curl_multi_cleanup(s->multi);
589
            s->multi = NULL;
590
        }
591
    }
592

593
    timer_del(&s->timer);
594
}
595

596
static void curl_attach_aio_context(BlockDriverState *bs,
597
                                    AioContext *new_context)
598
{
599
    BDRVCURLState *s = bs->opaque;
600

601
    aio_timer_init(new_context, &s->timer,
602
                   QEMU_CLOCK_REALTIME, SCALE_NS,
603
                   curl_multi_timeout_do, s);
604

605
    assert(!s->multi);
606
    s->multi = curl_multi_init();
607
    s->aio_context = new_context;
608
    curl_multi_setopt(s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb);
609
    curl_multi_setopt(s->multi, CURLMOPT_TIMERDATA, s);
610
    curl_multi_setopt(s->multi, CURLMOPT_TIMERFUNCTION, curl_timer_cb);
611
}
612

613
static QemuOptsList runtime_opts = {
614
    .name = "curl",
615
    .head = QTAILQ_HEAD_INITIALIZER(runtime_opts.head),
616
    .desc = {
617
        {
618
            .name = CURL_BLOCK_OPT_URL,
619
            .type = QEMU_OPT_STRING,
620
            .help = "URL to open",
621
        },
622
        {
623
            .name = CURL_BLOCK_OPT_READAHEAD,
624
            .type = QEMU_OPT_SIZE,
625
            .help = "Readahead size",
626
        },
627
        {
628
            .name = CURL_BLOCK_OPT_SSLVERIFY,
629
            .type = QEMU_OPT_BOOL,
630
            .help = "Verify SSL certificate"
631
        },
632
        {
633
            .name = CURL_BLOCK_OPT_TIMEOUT,
634
            .type = QEMU_OPT_NUMBER,
635
            .help = "Curl timeout"
636
        },
637
        {
638
            .name = CURL_BLOCK_OPT_COOKIE,
639
            .type = QEMU_OPT_STRING,
640
            .help = "Pass the cookie or list of cookies with each request"
641
        },
642
        {
643
            .name = CURL_BLOCK_OPT_COOKIE_SECRET,
644
            .type = QEMU_OPT_STRING,
645
            .help = "ID of secret used as cookie passed with each request"
646
        },
647
        {
648
            .name = CURL_BLOCK_OPT_USERNAME,
649
            .type = QEMU_OPT_STRING,
650
            .help = "Username for HTTP auth"
651
        },
652
        {
653
            .name = CURL_BLOCK_OPT_PASSWORD_SECRET,
654
            .type = QEMU_OPT_STRING,
655
            .help = "ID of secret used as password for HTTP auth",
656
        },
657
        {
658
            .name = CURL_BLOCK_OPT_PROXY_USERNAME,
659
            .type = QEMU_OPT_STRING,
660
            .help = "Username for HTTP proxy auth"
661
        },
662
        {
663
            .name = CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET,
664
            .type = QEMU_OPT_STRING,
665
            .help = "ID of secret used as password for HTTP proxy auth",
666
        },
667
        { /* end of list */ }
668
    },
669
};
670

671

672
static int curl_open(BlockDriverState *bs, QDict *options, int flags,
673
                     Error **errp)
674
{
675
    BDRVCURLState *s = bs->opaque;
676
    CURLState *state = NULL;
677
    QemuOpts *opts;
678
    const char *file;
679
    const char *cookie;
680
    const char *cookie_secret;
681
    /* CURL >= 7.55.0 uses curl_off_t for content length instead of a double */
682
#if LIBCURL_VERSION_NUM >= 0x073700
683
    curl_off_t cl;
684
#else
685
    double cl;
686
#endif
687
    const char *secretid;
688
    const char *protocol_delimiter;
689
    int ret;
690

691
    bdrv_graph_rdlock_main_loop();
692
    ret = bdrv_apply_auto_read_only(bs, "curl driver does not support writes",
693
                                    errp);
694
    bdrv_graph_rdunlock_main_loop();
695
    if (ret < 0) {
696
        return ret;
697
    }
698

699
    if (!libcurl_initialized) {
700
        ret = curl_global_init(CURL_GLOBAL_ALL);
701
        if (ret) {
702
            error_setg(errp, "libcurl initialization failed with %d", ret);
703
            return -EIO;
704
        }
705
        libcurl_initialized = true;
706
    }
707

708
    qemu_mutex_init(&s->mutex);
709
    opts = qemu_opts_create(&runtime_opts, NULL, 0, &error_abort);
710
    if (!qemu_opts_absorb_qdict(opts, options, errp)) {
711
        goto out_noclean;
712
    }
713

714
    s->readahead_size = qemu_opt_get_size(opts, CURL_BLOCK_OPT_READAHEAD,
715
                                          CURL_BLOCK_OPT_READAHEAD_DEFAULT);
716
    if ((s->readahead_size & 0x1ff) != 0) {
717
        error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512",
718
                   s->readahead_size);
719
        goto out_noclean;
720
    }
721

722
    s->timeout = qemu_opt_get_number(opts, CURL_BLOCK_OPT_TIMEOUT,
723
                                     CURL_BLOCK_OPT_TIMEOUT_DEFAULT);
724
    if (s->timeout > CURL_TIMEOUT_MAX) {
725
        error_setg(errp, "timeout parameter is too large or negative");
726
        goto out_noclean;
727
    }
728

729
    s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY,
730
                                     CURL_BLOCK_OPT_SSLVERIFY_DEFAULT);
731

732
    cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE);
733
    cookie_secret = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE_SECRET);
734

735
    if (cookie && cookie_secret) {
736
        error_setg(errp,
737
                   "curl driver cannot handle both cookie and cookie secret");
738
        goto out_noclean;
739
    }
740

741
    if (cookie_secret) {
742
        s->cookie = qcrypto_secret_lookup_as_utf8(cookie_secret, errp);
743
        if (!s->cookie) {
744
            goto out_noclean;
745
        }
746
    } else {
747
        s->cookie = g_strdup(cookie);
748
    }
749

750
    file = qemu_opt_get(opts, CURL_BLOCK_OPT_URL);
751
    if (file == NULL) {
752
        error_setg(errp, "curl block driver requires an 'url' option");
753
        goto out_noclean;
754
    }
755

756
    if (!strstart(file, bs->drv->protocol_name, &protocol_delimiter) ||
757
        !strstart(protocol_delimiter, "://", NULL))
758
    {
759
        error_setg(errp, "%s curl driver cannot handle the URL '%s' (does not "
760
                   "start with '%s://')", bs->drv->protocol_name, file,
761
                   bs->drv->protocol_name);
762
        goto out_noclean;
763
    }
764

765
    s->username = g_strdup(qemu_opt_get(opts, CURL_BLOCK_OPT_USERNAME));
766
    secretid = qemu_opt_get(opts, CURL_BLOCK_OPT_PASSWORD_SECRET);
767

768
    if (secretid) {
769
        s->password = qcrypto_secret_lookup_as_utf8(secretid, errp);
770
        if (!s->password) {
771
            goto out_noclean;
772
        }
773
    }
774

775
    s->proxyusername = g_strdup(
776
        qemu_opt_get(opts, CURL_BLOCK_OPT_PROXY_USERNAME));
777
    secretid = qemu_opt_get(opts, CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET);
778
    if (secretid) {
779
        s->proxypassword = qcrypto_secret_lookup_as_utf8(secretid, errp);
780
        if (!s->proxypassword) {
781
            goto out_noclean;
782
        }
783
    }
784

785
    trace_curl_open(file);
786
    qemu_co_queue_init(&s->free_state_waitq);
787
    s->aio_context = bdrv_get_aio_context(bs);
788
    s->url = g_strdup(file);
789
    s->sockets = g_hash_table_new_full(NULL, NULL, NULL, g_free);
790
    qemu_mutex_lock(&s->mutex);
791
    state = curl_find_state(s);
792
    qemu_mutex_unlock(&s->mutex);
793
    if (!state) {
794
        goto out_noclean;
795
    }
796

797
    // Get file size
798

799
    if (curl_init_state(s, state) < 0) {
800
        pstrcpy(state->errmsg, CURL_ERROR_SIZE,
801
                "curl library initialization failed.");
802
        goto out;
803
    }
804

805
    s->accept_range = false;
806
    if (curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1) ||
807
        curl_easy_setopt(state->curl, CURLOPT_HEADERFUNCTION, curl_header_cb) ||
808
        curl_easy_setopt(state->curl, CURLOPT_HEADERDATA, s)) {
809
        pstrcpy(state->errmsg, CURL_ERROR_SIZE,
810
                "curl library initialization failed.");
811
        goto out;
812
    }
813
    if (curl_easy_perform(state->curl))
814
        goto out;
815
    /* CURL 7.55.0 deprecates CURLINFO_CONTENT_LENGTH_DOWNLOAD in favour of
816
     * the *_T version which returns a more sensible type for content length.
817
     */
818
#if LIBCURL_VERSION_NUM >= 0x073700
819
    if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &cl)) {
820
        goto out;
821
    }
822
#else
823
    if (curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &cl)) {
824
        goto out;
825
    }
826
#endif
827
    /* Prior CURL 7.19.4 return value of 0 could mean that the file size is not
828
     * know or the size is zero. From 7.19.4 CURL returns -1 if size is not
829
     * known and zero if it is really zero-length file. */
830
#if LIBCURL_VERSION_NUM >= 0x071304
831
    if (cl < 0) {
832
        pstrcpy(state->errmsg, CURL_ERROR_SIZE,
833
                "Server didn't report file size.");
834
        goto out;
835
    }
836
#else
837
    if (cl <= 0) {
838
        pstrcpy(state->errmsg, CURL_ERROR_SIZE,
839
                "Unknown file size or zero-length file.");
840
        goto out;
841
    }
842
#endif
843

844
    s->len = cl;
845

846
    if ((!strncasecmp(s->url, "http://", strlen("http://"))
847
        || !strncasecmp(s->url, "https://", strlen("https://")))
848
        && !s->accept_range) {
849
        pstrcpy(state->errmsg, CURL_ERROR_SIZE,
850
                "Server does not support 'range' (byte ranges).");
851
        goto out;
852
    }
853
    trace_curl_open_size(s->len);
854

855
    qemu_mutex_lock(&s->mutex);
856
    curl_clean_state(state);
857
    qemu_mutex_unlock(&s->mutex);
858
    curl_easy_cleanup(state->curl);
859
    state->curl = NULL;
860

861
    curl_attach_aio_context(bs, bdrv_get_aio_context(bs));
862

863
    qemu_opts_del(opts);
864
    return 0;
865

866
out:
867
    error_setg(errp, "CURL: Error opening file: %s", state->errmsg);
868
    curl_easy_cleanup(state->curl);
869
    state->curl = NULL;
870
out_noclean:
871
    qemu_mutex_destroy(&s->mutex);
872
    g_free(s->cookie);
873
    g_free(s->url);
874
    g_free(s->username);
875
    g_free(s->proxyusername);
876
    g_free(s->proxypassword);
877
    if (s->sockets) {
878
        curl_drop_all_sockets(s->sockets);
879
        g_hash_table_destroy(s->sockets);
880
    }
881
    qemu_opts_del(opts);
882
    return -EINVAL;
883
}
884

885
static void coroutine_fn curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb)
886
{
887
    CURLState *state;
888
    int running;
889

890
    BDRVCURLState *s = bs->opaque;
891

892
    uint64_t start = acb->offset;
893
    uint64_t end;
894

895
    qemu_mutex_lock(&s->mutex);
896

897
    // In case we have the requested data already (e.g. read-ahead),
898
    // we can just call the callback and be done.
899
    if (curl_find_buf(s, start, acb->bytes, acb)) {
900
        goto out;
901
    }
902

903
    // No cache found, so let's start a new request
904
    for (;;) {
905
        state = curl_find_state(s);
906
        if (state) {
907
            break;
908
        }
909
        qemu_co_queue_wait(&s->free_state_waitq, &s->mutex);
910
    }
911

912
    if (curl_init_state(s, state) < 0) {
913
        curl_clean_state(state);
914
        acb->ret = -EIO;
915
        goto out;
916
    }
917

918
    acb->start = 0;
919
    acb->end = MIN(acb->bytes, s->len - start);
920

921
    state->buf_off = 0;
922
    g_free(state->orig_buf);
923
    state->buf_start = start;
924
    state->buf_len = MIN(acb->end + s->readahead_size, s->len - start);
925
    end = start + state->buf_len - 1;
926
    state->orig_buf = g_try_malloc(state->buf_len);
927
    if (state->buf_len && state->orig_buf == NULL) {
928
        curl_clean_state(state);
929
        acb->ret = -ENOMEM;
930
        goto out;
931
    }
932
    state->acb[0] = acb;
933

934
    snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end);
935
    trace_curl_setup_preadv(acb->bytes, start, state->range);
936
    if (curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range) ||
937
        curl_multi_add_handle(s->multi, state->curl) != CURLM_OK) {
938
        state->acb[0] = NULL;
939
        acb->ret = -EIO;
940

941
        curl_clean_state(state);
942
        goto out;
943
    }
944

945
    /* Tell curl it needs to kick things off */
946
    curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
947

948
out:
949
    qemu_mutex_unlock(&s->mutex);
950
}
951

952
static int coroutine_fn curl_co_preadv(BlockDriverState *bs,
953
        int64_t offset, int64_t bytes, QEMUIOVector *qiov,
954
        BdrvRequestFlags flags)
955
{
956
    CURLAIOCB acb = {
957
        .co = qemu_coroutine_self(),
958
        .ret = -EINPROGRESS,
959
        .qiov = qiov,
960
        .offset = offset,
961
        .bytes = bytes
962
    };
963

964
    curl_setup_preadv(bs, &acb);
965
    while (acb.ret == -EINPROGRESS) {
966
        qemu_coroutine_yield();
967
    }
968
    return acb.ret;
969
}
970

971
static void curl_close(BlockDriverState *bs)
972
{
973
    BDRVCURLState *s = bs->opaque;
974

975
    trace_curl_close();
976
    curl_detach_aio_context(bs);
977
    qemu_mutex_destroy(&s->mutex);
978

979
    g_hash_table_destroy(s->sockets);
980
    g_free(s->cookie);
981
    g_free(s->url);
982
    g_free(s->username);
983
    g_free(s->proxyusername);
984
    g_free(s->proxypassword);
985
}
986

987
static int64_t coroutine_fn curl_co_getlength(BlockDriverState *bs)
988
{
989
    BDRVCURLState *s = bs->opaque;
990
    return s->len;
991
}
992

993
static void curl_refresh_filename(BlockDriverState *bs)
994
{
995
    BDRVCURLState *s = bs->opaque;
996

997
    /* "readahead" and "timeout" do not change the guest-visible data,
998
     * so ignore them */
999
    if (s->sslverify != CURL_BLOCK_OPT_SSLVERIFY_DEFAULT ||
1000
        s->cookie || s->username || s->password || s->proxyusername ||
1001
        s->proxypassword)
1002
    {
1003
        return;
1004
    }
1005

1006
    pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), s->url);
1007
}
1008

1009

1010
static const char *const curl_strong_runtime_opts[] = {
1011
    CURL_BLOCK_OPT_URL,
1012
    CURL_BLOCK_OPT_SSLVERIFY,
1013
    CURL_BLOCK_OPT_COOKIE,
1014
    CURL_BLOCK_OPT_COOKIE_SECRET,
1015
    CURL_BLOCK_OPT_USERNAME,
1016
    CURL_BLOCK_OPT_PASSWORD_SECRET,
1017
    CURL_BLOCK_OPT_PROXY_USERNAME,
1018
    CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET,
1019

1020
    NULL
1021
};
1022

1023
static BlockDriver bdrv_http = {
1024
    .format_name                = "http",
1025
    .protocol_name              = "http",
1026

1027
    .instance_size              = sizeof(BDRVCURLState),
1028
    .bdrv_parse_filename        = curl_parse_filename,
1029
    .bdrv_open                  = curl_open,
1030
    .bdrv_close                 = curl_close,
1031
    .bdrv_co_getlength          = curl_co_getlength,
1032

1033
    .bdrv_co_preadv             = curl_co_preadv,
1034

1035
    .bdrv_detach_aio_context    = curl_detach_aio_context,
1036
    .bdrv_attach_aio_context    = curl_attach_aio_context,
1037

1038
    .bdrv_refresh_filename      = curl_refresh_filename,
1039
    .strong_runtime_opts        = curl_strong_runtime_opts,
1040
};
1041

1042
static BlockDriver bdrv_https = {
1043
    .format_name                = "https",
1044
    .protocol_name              = "https",
1045

1046
    .instance_size              = sizeof(BDRVCURLState),
1047
    .bdrv_parse_filename        = curl_parse_filename,
1048
    .bdrv_open                  = curl_open,
1049
    .bdrv_close                 = curl_close,
1050
    .bdrv_co_getlength          = curl_co_getlength,
1051

1052
    .bdrv_co_preadv             = curl_co_preadv,
1053

1054
    .bdrv_detach_aio_context    = curl_detach_aio_context,
1055
    .bdrv_attach_aio_context    = curl_attach_aio_context,
1056

1057
    .bdrv_refresh_filename      = curl_refresh_filename,
1058
    .strong_runtime_opts        = curl_strong_runtime_opts,
1059
};
1060

1061
static BlockDriver bdrv_ftp = {
1062
    .format_name                = "ftp",
1063
    .protocol_name              = "ftp",
1064

1065
    .instance_size              = sizeof(BDRVCURLState),
1066
    .bdrv_parse_filename        = curl_parse_filename,
1067
    .bdrv_open                  = curl_open,
1068
    .bdrv_close                 = curl_close,
1069
    .bdrv_co_getlength          = curl_co_getlength,
1070

1071
    .bdrv_co_preadv             = curl_co_preadv,
1072

1073
    .bdrv_detach_aio_context    = curl_detach_aio_context,
1074
    .bdrv_attach_aio_context    = curl_attach_aio_context,
1075

1076
    .bdrv_refresh_filename      = curl_refresh_filename,
1077
    .strong_runtime_opts        = curl_strong_runtime_opts,
1078
};
1079

1080
static BlockDriver bdrv_ftps = {
1081
    .format_name                = "ftps",
1082
    .protocol_name              = "ftps",
1083

1084
    .instance_size              = sizeof(BDRVCURLState),
1085
    .bdrv_parse_filename        = curl_parse_filename,
1086
    .bdrv_open                  = curl_open,
1087
    .bdrv_close                 = curl_close,
1088
    .bdrv_co_getlength          = curl_co_getlength,
1089

1090
    .bdrv_co_preadv             = curl_co_preadv,
1091

1092
    .bdrv_detach_aio_context    = curl_detach_aio_context,
1093
    .bdrv_attach_aio_context    = curl_attach_aio_context,
1094

1095
    .bdrv_refresh_filename      = curl_refresh_filename,
1096
    .strong_runtime_opts        = curl_strong_runtime_opts,
1097
};
1098

1099
static void curl_block_init(void)
1100
{
1101
    bdrv_register(&bdrv_http);
1102
    bdrv_register(&bdrv_https);
1103
    bdrv_register(&bdrv_ftp);
1104
    bdrv_register(&bdrv_ftps);
1105
}
1106

1107
block_init(curl_block_init);
1108

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.