xref: /openbmc/qemu/block/curl.c (revision 41bd3603)
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 #include "qemu-common.h"
25 #include "block_int.h"
26 #include <curl/curl.h>
27 
28 // #define DEBUG
29 // #define DEBUG_VERBOSE
30 
31 #ifdef DEBUG_CURL
32 #define DPRINTF(fmt, ...) do { printf(fmt, ## __VA_ARGS__); } while (0)
33 #else
34 #define DPRINTF(fmt, ...) do { } while (0)
35 #endif
36 
37 #define CURL_NUM_STATES 8
38 #define CURL_NUM_ACB    8
39 #define SECTOR_SIZE     512
40 #define READ_AHEAD_SIZE (256 * 1024)
41 
42 #define FIND_RET_NONE   0
43 #define FIND_RET_OK     1
44 #define FIND_RET_WAIT   2
45 
46 struct BDRVCURLState;
47 
48 typedef struct CURLAIOCB {
49     BlockDriverAIOCB common;
50     QEMUBH *bh;
51     QEMUIOVector *qiov;
52 
53     int64_t sector_num;
54     int nb_sectors;
55 
56     size_t start;
57     size_t end;
58 } CURLAIOCB;
59 
60 typedef struct CURLState
61 {
62     struct BDRVCURLState *s;
63     CURLAIOCB *acb[CURL_NUM_ACB];
64     CURL *curl;
65     char *orig_buf;
66     size_t buf_start;
67     size_t buf_off;
68     size_t buf_len;
69     char range[128];
70     char errmsg[CURL_ERROR_SIZE];
71     char in_use;
72 } CURLState;
73 
74 typedef struct BDRVCURLState {
75     CURLM *multi;
76     size_t len;
77     CURLState states[CURL_NUM_STATES];
78     char *url;
79     size_t readahead_size;
80 } BDRVCURLState;
81 
82 static void curl_clean_state(CURLState *s);
83 static void curl_multi_do(void *arg);
84 static int curl_aio_flush(void *opaque);
85 
86 static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action,
87                         void *s, void *sp)
88 {
89     DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, fd);
90     switch (action) {
91         case CURL_POLL_IN:
92             qemu_aio_set_fd_handler(fd, curl_multi_do, NULL, curl_aio_flush,
93                                     NULL, s);
94             break;
95         case CURL_POLL_OUT:
96             qemu_aio_set_fd_handler(fd, NULL, curl_multi_do, curl_aio_flush,
97                                     NULL, s);
98             break;
99         case CURL_POLL_INOUT:
100             qemu_aio_set_fd_handler(fd, curl_multi_do, curl_multi_do,
101                                     curl_aio_flush, NULL, s);
102             break;
103         case CURL_POLL_REMOVE:
104             qemu_aio_set_fd_handler(fd, NULL, NULL, NULL, NULL, NULL);
105             break;
106     }
107 
108     return 0;
109 }
110 
111 static size_t curl_size_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
112 {
113     CURLState *s = ((CURLState*)opaque);
114     size_t realsize = size * nmemb;
115     size_t fsize;
116 
117     if(sscanf(ptr, "Content-Length: %zd", &fsize) == 1) {
118         s->s->len = fsize;
119     }
120 
121     return realsize;
122 }
123 
124 static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
125 {
126     CURLState *s = ((CURLState*)opaque);
127     size_t realsize = size * nmemb;
128     int i;
129 
130     DPRINTF("CURL: Just reading %zd bytes\n", realsize);
131 
132     if (!s || !s->orig_buf)
133         goto read_end;
134 
135     memcpy(s->orig_buf + s->buf_off, ptr, realsize);
136     s->buf_off += realsize;
137 
138     for(i=0; i<CURL_NUM_ACB; i++) {
139         CURLAIOCB *acb = s->acb[i];
140 
141         if (!acb)
142             continue;
143 
144         if ((s->buf_off >= acb->end)) {
145             qemu_iovec_from_buffer(acb->qiov, s->orig_buf + acb->start,
146                                    acb->end - acb->start);
147             acb->common.cb(acb->common.opaque, 0);
148             qemu_aio_release(acb);
149             s->acb[i] = NULL;
150         }
151     }
152 
153 read_end:
154     return realsize;
155 }
156 
157 static int curl_find_buf(BDRVCURLState *s, size_t start, size_t len,
158                          CURLAIOCB *acb)
159 {
160     int i;
161     size_t end = start + len;
162 
163     for (i=0; i<CURL_NUM_STATES; i++) {
164         CURLState *state = &s->states[i];
165         size_t buf_end = (state->buf_start + state->buf_off);
166         size_t buf_fend = (state->buf_start + state->buf_len);
167 
168         if (!state->orig_buf)
169             continue;
170         if (!state->buf_off)
171             continue;
172 
173         // Does the existing buffer cover our section?
174         if ((start >= state->buf_start) &&
175             (start <= buf_end) &&
176             (end >= state->buf_start) &&
177             (end <= buf_end))
178         {
179             char *buf = state->orig_buf + (start - state->buf_start);
180 
181             qemu_iovec_from_buffer(acb->qiov, buf, len);
182             acb->common.cb(acb->common.opaque, 0);
183 
184             return FIND_RET_OK;
185         }
186 
187         // Wait for unfinished chunks
188         if ((start >= state->buf_start) &&
189             (start <= buf_fend) &&
190             (end >= state->buf_start) &&
191             (end <= buf_fend))
192         {
193             int j;
194 
195             acb->start = start - state->buf_start;
196             acb->end = acb->start + len;
197 
198             for (j=0; j<CURL_NUM_ACB; j++) {
199                 if (!state->acb[j]) {
200                     state->acb[j] = acb;
201                     return FIND_RET_WAIT;
202                 }
203             }
204         }
205     }
206 
207     return FIND_RET_NONE;
208 }
209 
210 static void curl_multi_do(void *arg)
211 {
212     BDRVCURLState *s = (BDRVCURLState *)arg;
213     int running;
214     int r;
215     int msgs_in_queue;
216 
217     if (!s->multi)
218         return;
219 
220     do {
221         r = curl_multi_socket_all(s->multi, &running);
222     } while(r == CURLM_CALL_MULTI_PERFORM);
223 
224     /* Try to find done transfers, so we can free the easy
225      * handle again. */
226     do {
227         CURLMsg *msg;
228         msg = curl_multi_info_read(s->multi, &msgs_in_queue);
229 
230         if (!msg)
231             break;
232         if (msg->msg == CURLMSG_NONE)
233             break;
234 
235         switch (msg->msg) {
236             case CURLMSG_DONE:
237             {
238                 CURLState *state = NULL;
239                 curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, (char**)&state);
240 
241                 /* ACBs for successful messages get completed in curl_read_cb */
242                 if (msg->data.result != CURLE_OK) {
243                     int i;
244                     for (i = 0; i < CURL_NUM_ACB; i++) {
245                         CURLAIOCB *acb = state->acb[i];
246 
247                         if (acb == NULL) {
248                             continue;
249                         }
250 
251                         acb->common.cb(acb->common.opaque, -EIO);
252                         qemu_aio_release(acb);
253                         state->acb[i] = NULL;
254                     }
255                 }
256 
257                 curl_clean_state(state);
258                 break;
259             }
260             default:
261                 msgs_in_queue = 0;
262                 break;
263         }
264     } while(msgs_in_queue);
265 }
266 
267 static CURLState *curl_init_state(BDRVCURLState *s)
268 {
269     CURLState *state = NULL;
270     int i, j;
271 
272     do {
273         for (i=0; i<CURL_NUM_STATES; i++) {
274             for (j=0; j<CURL_NUM_ACB; j++)
275                 if (s->states[i].acb[j])
276                     continue;
277             if (s->states[i].in_use)
278                 continue;
279 
280             state = &s->states[i];
281             state->in_use = 1;
282             break;
283         }
284         if (!state) {
285             usleep(100);
286             curl_multi_do(s);
287         }
288     } while(!state);
289 
290     if (state->curl)
291         goto has_curl;
292 
293     state->curl = curl_easy_init();
294     if (!state->curl)
295         return NULL;
296     curl_easy_setopt(state->curl, CURLOPT_URL, s->url);
297     curl_easy_setopt(state->curl, CURLOPT_TIMEOUT, 5);
298     curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
299     curl_easy_setopt(state->curl, CURLOPT_WRITEDATA, (void *)state);
300     curl_easy_setopt(state->curl, CURLOPT_PRIVATE, (void *)state);
301     curl_easy_setopt(state->curl, CURLOPT_AUTOREFERER, 1);
302     curl_easy_setopt(state->curl, CURLOPT_FOLLOWLOCATION, 1);
303     curl_easy_setopt(state->curl, CURLOPT_NOSIGNAL, 1);
304     curl_easy_setopt(state->curl, CURLOPT_ERRORBUFFER, state->errmsg);
305     curl_easy_setopt(state->curl, CURLOPT_FAILONERROR, 1);
306 
307 #ifdef DEBUG_VERBOSE
308     curl_easy_setopt(state->curl, CURLOPT_VERBOSE, 1);
309 #endif
310 
311 has_curl:
312 
313     state->s = s;
314 
315     return state;
316 }
317 
318 static void curl_clean_state(CURLState *s)
319 {
320     if (s->s->multi)
321         curl_multi_remove_handle(s->s->multi, s->curl);
322     s->in_use = 0;
323 }
324 
325 static int curl_open(BlockDriverState *bs, const char *filename, int flags)
326 {
327     BDRVCURLState *s = bs->opaque;
328     CURLState *state = NULL;
329     double d;
330 
331     #define RA_OPTSTR ":readahead="
332     char *file;
333     char *ra;
334     const char *ra_val;
335     int parse_state = 0;
336 
337     static int inited = 0;
338 
339     file = g_strdup(filename);
340     s->readahead_size = READ_AHEAD_SIZE;
341 
342     /* Parse a trailing ":readahead=#:" param, if present. */
343     ra = file + strlen(file) - 1;
344     while (ra >= file) {
345         if (parse_state == 0) {
346             if (*ra == ':')
347                 parse_state++;
348             else
349                 break;
350         } else if (parse_state == 1) {
351             if (*ra > '9' || *ra < '0') {
352                 char *opt_start = ra - strlen(RA_OPTSTR) + 1;
353                 if (opt_start > file &&
354                     strncmp(opt_start, RA_OPTSTR, strlen(RA_OPTSTR)) == 0) {
355                     ra_val = ra + 1;
356                     ra -= strlen(RA_OPTSTR) - 1;
357                     *ra = '\0';
358                     s->readahead_size = atoi(ra_val);
359                     break;
360                 } else {
361                     break;
362                 }
363             }
364         }
365         ra--;
366     }
367 
368     if ((s->readahead_size & 0x1ff) != 0) {
369         fprintf(stderr, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512\n",
370                 s->readahead_size);
371         goto out_noclean;
372     }
373 
374     if (!inited) {
375         curl_global_init(CURL_GLOBAL_ALL);
376         inited = 1;
377     }
378 
379     DPRINTF("CURL: Opening %s\n", file);
380     s->url = file;
381     state = curl_init_state(s);
382     if (!state)
383         goto out_noclean;
384 
385     // Get file size
386 
387     curl_easy_setopt(state->curl, CURLOPT_NOBODY, 1);
388     curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_size_cb);
389     if (curl_easy_perform(state->curl))
390         goto out;
391     curl_easy_getinfo(state->curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d);
392     curl_easy_setopt(state->curl, CURLOPT_WRITEFUNCTION, (void *)curl_read_cb);
393     curl_easy_setopt(state->curl, CURLOPT_NOBODY, 0);
394     if (d)
395         s->len = (size_t)d;
396     else if(!s->len)
397         goto out;
398     DPRINTF("CURL: Size = %zd\n", s->len);
399 
400     curl_clean_state(state);
401     curl_easy_cleanup(state->curl);
402     state->curl = NULL;
403 
404     // Now we know the file exists and its size, so let's
405     // initialize the multi interface!
406 
407     s->multi = curl_multi_init();
408     curl_multi_setopt( s->multi, CURLMOPT_SOCKETDATA, s);
409     curl_multi_setopt( s->multi, CURLMOPT_SOCKETFUNCTION, curl_sock_cb );
410     curl_multi_do(s);
411 
412     return 0;
413 
414 out:
415     fprintf(stderr, "CURL: Error opening file: %s\n", state->errmsg);
416     curl_easy_cleanup(state->curl);
417     state->curl = NULL;
418 out_noclean:
419     g_free(file);
420     return -EINVAL;
421 }
422 
423 static int curl_aio_flush(void *opaque)
424 {
425     BDRVCURLState *s = opaque;
426     int i, j;
427 
428     for (i=0; i < CURL_NUM_STATES; i++) {
429         for(j=0; j < CURL_NUM_ACB; j++) {
430             if (s->states[i].acb[j]) {
431                 return 1;
432             }
433         }
434     }
435     return 0;
436 }
437 
438 static void curl_aio_cancel(BlockDriverAIOCB *blockacb)
439 {
440     // Do we have to implement canceling? Seems to work without...
441 }
442 
443 static AIOPool curl_aio_pool = {
444     .aiocb_size         = sizeof(CURLAIOCB),
445     .cancel             = curl_aio_cancel,
446 };
447 
448 
449 static void curl_readv_bh_cb(void *p)
450 {
451     CURLState *state;
452 
453     CURLAIOCB *acb = p;
454     BDRVCURLState *s = acb->common.bs->opaque;
455 
456     qemu_bh_delete(acb->bh);
457     acb->bh = NULL;
458 
459     size_t start = acb->sector_num * SECTOR_SIZE;
460     size_t end;
461 
462     // In case we have the requested data already (e.g. read-ahead),
463     // we can just call the callback and be done.
464     switch (curl_find_buf(s, start, acb->nb_sectors * SECTOR_SIZE, acb)) {
465         case FIND_RET_OK:
466             qemu_aio_release(acb);
467             // fall through
468         case FIND_RET_WAIT:
469             return;
470         default:
471             break;
472     }
473 
474     // No cache found, so let's start a new request
475     state = curl_init_state(s);
476     if (!state) {
477         acb->common.cb(acb->common.opaque, -EIO);
478         qemu_aio_release(acb);
479         return;
480     }
481 
482     acb->start = 0;
483     acb->end = (acb->nb_sectors * SECTOR_SIZE);
484 
485     state->buf_off = 0;
486     if (state->orig_buf)
487         g_free(state->orig_buf);
488     state->buf_start = start;
489     state->buf_len = acb->end + s->readahead_size;
490     end = MIN(start + state->buf_len, s->len) - 1;
491     state->orig_buf = g_malloc(state->buf_len);
492     state->acb[0] = acb;
493 
494     snprintf(state->range, 127, "%zd-%zd", start, end);
495     DPRINTF("CURL (AIO): Reading %d at %zd (%s)\n",
496             (acb->nb_sectors * SECTOR_SIZE), start, state->range);
497     curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range);
498 
499     curl_multi_add_handle(s->multi, state->curl);
500     curl_multi_do(s);
501 
502 }
503 
504 static BlockDriverAIOCB *curl_aio_readv(BlockDriverState *bs,
505         int64_t sector_num, QEMUIOVector *qiov, int nb_sectors,
506         BlockDriverCompletionFunc *cb, void *opaque)
507 {
508     CURLAIOCB *acb;
509 
510     acb = qemu_aio_get(&curl_aio_pool, bs, cb, opaque);
511 
512     acb->qiov = qiov;
513     acb->sector_num = sector_num;
514     acb->nb_sectors = nb_sectors;
515 
516     acb->bh = qemu_bh_new(curl_readv_bh_cb, acb);
517 
518     if (!acb->bh) {
519         DPRINTF("CURL: qemu_bh_new failed\n");
520         return NULL;
521     }
522 
523     qemu_bh_schedule(acb->bh);
524     return &acb->common;
525 }
526 
527 static void curl_close(BlockDriverState *bs)
528 {
529     BDRVCURLState *s = bs->opaque;
530     int i;
531 
532     DPRINTF("CURL: Close\n");
533     for (i=0; i<CURL_NUM_STATES; i++) {
534         if (s->states[i].in_use)
535             curl_clean_state(&s->states[i]);
536         if (s->states[i].curl) {
537             curl_easy_cleanup(s->states[i].curl);
538             s->states[i].curl = NULL;
539         }
540         if (s->states[i].orig_buf) {
541             g_free(s->states[i].orig_buf);
542             s->states[i].orig_buf = NULL;
543         }
544     }
545     if (s->multi)
546         curl_multi_cleanup(s->multi);
547     if (s->url)
548         free(s->url);
549 }
550 
551 static int64_t curl_getlength(BlockDriverState *bs)
552 {
553     BDRVCURLState *s = bs->opaque;
554     return s->len;
555 }
556 
557 static BlockDriver bdrv_http = {
558     .format_name     = "http",
559     .protocol_name   = "http",
560 
561     .instance_size   = sizeof(BDRVCURLState),
562     .bdrv_file_open  = curl_open,
563     .bdrv_close      = curl_close,
564     .bdrv_getlength  = curl_getlength,
565 
566     .bdrv_aio_readv  = curl_aio_readv,
567 };
568 
569 static BlockDriver bdrv_https = {
570     .format_name     = "https",
571     .protocol_name   = "https",
572 
573     .instance_size   = sizeof(BDRVCURLState),
574     .bdrv_file_open  = curl_open,
575     .bdrv_close      = curl_close,
576     .bdrv_getlength  = curl_getlength,
577 
578     .bdrv_aio_readv  = curl_aio_readv,
579 };
580 
581 static BlockDriver bdrv_ftp = {
582     .format_name     = "ftp",
583     .protocol_name   = "ftp",
584 
585     .instance_size   = sizeof(BDRVCURLState),
586     .bdrv_file_open  = curl_open,
587     .bdrv_close      = curl_close,
588     .bdrv_getlength  = curl_getlength,
589 
590     .bdrv_aio_readv  = curl_aio_readv,
591 };
592 
593 static BlockDriver bdrv_ftps = {
594     .format_name     = "ftps",
595     .protocol_name   = "ftps",
596 
597     .instance_size   = sizeof(BDRVCURLState),
598     .bdrv_file_open  = curl_open,
599     .bdrv_close      = curl_close,
600     .bdrv_getlength  = curl_getlength,
601 
602     .bdrv_aio_readv  = curl_aio_readv,
603 };
604 
605 static BlockDriver bdrv_tftp = {
606     .format_name     = "tftp",
607     .protocol_name   = "tftp",
608 
609     .instance_size   = sizeof(BDRVCURLState),
610     .bdrv_file_open  = curl_open,
611     .bdrv_close      = curl_close,
612     .bdrv_getlength  = curl_getlength,
613 
614     .bdrv_aio_readv  = curl_aio_readv,
615 };
616 
617 static void curl_block_init(void)
618 {
619     bdrv_register(&bdrv_http);
620     bdrv_register(&bdrv_https);
621     bdrv_register(&bdrv_ftp);
622     bdrv_register(&bdrv_ftps);
623     bdrv_register(&bdrv_tftp);
624 }
625 
626 block_init(curl_block_init);
627